From d56fae1095a28b44df224e3521a703e077b61df8 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Wed, 3 Dec 2025 17:22:29 +0700 Subject: [PATCH 1/3] address deprecated UITextItemInteraction --- .../Privacy/PrivacySettingsViewController.swift | 12 ++++++++---- .../Settings/Settings/SettingsViewController.swift | 11 +++++++---- .../SwiftUI Components/AttributedText.swift | 10 +++++++--- .../ReusableViews/TextViewTableViewCell.swift | 14 +++++++++----- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacySettingsViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacySettingsViewController.swift index 50ce3b3dc62..605b2582b2b 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacySettingsViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacySettingsViewController.swift @@ -501,9 +501,13 @@ private enum Row: CaseIterable { } extension PrivacySettingsViewController: UITextViewDelegate { - func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { - presentURL(URL) - trackURLPresentation(URL) - return false + func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? { + if case .link(let url) = textItem.content { + presentURL(url) + trackURLPresentation(url) + // Prevent default action + return nil + } + return defaultAction } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Settings/SettingsViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Settings/SettingsViewController.swift index 7fd48047ae2..9163dfec8cb 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Settings/SettingsViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Settings/SettingsViewController.swift @@ -562,10 +562,13 @@ private extension SettingsViewController { // extension SettingsViewController: UITextViewDelegate { - func textView(_ textView: UITextView, shouldInteractWith URL: URL, - in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { - weAreHiringWasPressed(url: URL) - return false + func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? { + if case .link(let url) = textItem.content { + weAreHiringWasPressed(url: url) + // Prevent default action + return nil + } + return defaultAction } } diff --git a/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/AttributedText.swift b/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/AttributedText.swift index 2b8ecfabc7c..ca1bf24f280 100644 --- a/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/AttributedText.swift +++ b/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/AttributedText.swift @@ -123,9 +123,13 @@ private struct TextViewWrapper: UIViewRepresentable { final class Coordinator: NSObject, UITextViewDelegate { var openURL: ((URL) -> Void)? - func textView(_: UITextView, shouldInteractWith URL: URL, in _: NSRange, interaction _: UITextItemInteraction) -> Bool { - openURL?(URL) - return false + func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? { + if case .link(let url) = textItem.content { + openURL?(url) + // Prevent default action + return nil + } + return defaultAction } } diff --git a/WooCommerce/Classes/ViewRelated/ReusableViews/TextViewTableViewCell.swift b/WooCommerce/Classes/ViewRelated/ReusableViews/TextViewTableViewCell.swift index ed91fcd5b8c..b77f4ef5c9b 100644 --- a/WooCommerce/Classes/ViewRelated/ReusableViews/TextViewTableViewCell.swift +++ b/WooCommerce/Classes/ViewRelated/ReusableViews/TextViewTableViewCell.swift @@ -118,12 +118,16 @@ final class TextViewTableViewCell: UITableViewCell { } extension TextViewTableViewCell: UITextViewDelegate { - func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { - if let onLinkTapped { - onLinkTapped(URL) - return false + func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? { + if case .link(let url) = textItem.content { + if let onLinkTapped { + onLinkTapped(url) + // Prevent default action + return nil + } } - return true + // Allow default behavior + return defaultAction } } From 58414344f9cba3fc86a4d8e5538dfc9c01fb3c85 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Wed, 3 Dec 2025 17:25:36 +0700 Subject: [PATCH 2/3] update usage of traitCollectionDidChange --- .../RoleErrorViewController.swift | 31 +++++++++---------- .../ULAccountMismatchViewController.swift | 8 +++-- .../ULErrorViewController.swift | 8 +++-- .../FilterTabBar/FilterTabBar.swift | 10 +++--- .../ViewRelated/MainTabBarController.swift | 13 ++++---- .../SurveySubmittedViewController.swift | 16 +++++----- .../Top Banner/TopBannerView.swift | 16 +++++----- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift b/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift index 46bef726380..4de99b30262 100644 --- a/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift +++ b/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift @@ -61,6 +61,7 @@ class RoleErrorViewController: UIViewController { super.viewDidLoad() configureViews() + observeTraitChanges() } override func viewWillAppear(_ animated: Bool) { @@ -95,6 +96,19 @@ class RoleErrorViewController: UIViewController { configureLinkButton() configurePrimaryActionButton() configureSecondaryActionButton() + + // Set initial image visibility based on vertical size class + imageView.isHidden = traitCollection.verticalSizeClass == .compact + } + + private func observeTraitChanges() { + registerForTraitChanges([UITraitVerticalSizeClass.self, UITraitUserInterfaceStyle.self]) { (self: Self, _) in + // Hide image in compact height sizes (e.g. landscape iPhones) + // With limited space, text description should have higher priority + self.imageView.isHidden = self.traitCollection.verticalSizeClass == .compact + + self.updateViewAppearances() + } } func configureDescriptionLabel() { @@ -128,24 +142,9 @@ class RoleErrorViewController: UIViewController { } } - // MARK: Trait Change Adjustments - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - // hide image in compact height sizes (e.g. landscape iphones). - // with limited space, text description should have higher priority. - imageView.isHidden = traitCollection.verticalSizeClass == .compact - - // handle dynamic color appearance changes. - if let previousTrait = previousTraitCollection, - previousTrait.hasDifferentColorAppearance(comparedTo: traitCollection) { - updateViewAppearances() - } - } /// update views that can adjust to color appearance changes. - /// this method is called when color appearance changes are detected in `traitCollectionDidChange`. + /// this method is called when color appearance changes are detected via trait change registration. private func updateViewAppearances() { // illustrations imageView.image = viewModel.image diff --git a/WooCommerce/Classes/Authentication/Navigation Exceptions/ULAccountMismatchViewController.swift b/WooCommerce/Classes/Authentication/Navigation Exceptions/ULAccountMismatchViewController.swift index 492b21122d9..7a32da03c75 100644 --- a/WooCommerce/Classes/Authentication/Navigation Exceptions/ULAccountMismatchViewController.swift +++ b/WooCommerce/Classes/Authentication/Navigation Exceptions/ULAccountMismatchViewController.swift @@ -62,13 +62,15 @@ final class ULAccountMismatchViewController: UIViewController { configureSecondaryButon() setUnifiedMargins(forWidth: view.frame.width) + observeTraitChanges() viewModel.viewDidLoad(self) } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - setUnifiedMargins(forWidth: view.frame.width) + private func observeTraitChanges() { + registerForTraitChanges([UITraitHorizontalSizeClass.self, UITraitVerticalSizeClass.self]) { (self: Self, _) in + self.setUnifiedMargins(forWidth: self.view.frame.width) + } } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { diff --git a/WooCommerce/Classes/Authentication/Navigation Exceptions/ULErrorViewController.swift b/WooCommerce/Classes/Authentication/Navigation Exceptions/ULErrorViewController.swift index 23511e2a871..64dc76cea24 100644 --- a/WooCommerce/Classes/Authentication/Navigation Exceptions/ULErrorViewController.swift +++ b/WooCommerce/Classes/Authentication/Navigation Exceptions/ULErrorViewController.swift @@ -72,6 +72,7 @@ final class ULErrorViewController: UIViewController { configureButtonLabels() setUnifiedMargins(forWidth: view.frame.width) + observeTraitChanges() viewModel.viewDidLoad(self) } @@ -81,9 +82,10 @@ final class ULErrorViewController: UIViewController { viewDidAppearSubject.send() } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - setUnifiedMargins(forWidth: view.frame.width) + private func observeTraitChanges() { + registerForTraitChanges([UITraitHorizontalSizeClass.self, UITraitVerticalSizeClass.self]) { (self: Self, _) in + self.setUnifiedMargins(forWidth: self.view.frame.width) + } } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { diff --git a/WooCommerce/Classes/ViewRelated/FilterTabBar/FilterTabBar.swift b/WooCommerce/Classes/ViewRelated/FilterTabBar/FilterTabBar.swift index bfcdab2b218..411a60f0853 100644 --- a/WooCommerce/Classes/ViewRelated/FilterTabBar/FilterTabBar.swift +++ b/WooCommerce/Classes/ViewRelated/FilterTabBar/FilterTabBar.swift @@ -518,23 +518,23 @@ private class TabBarButton: UIButton { super.init(frame: frame) setFont() + observeTraitChanges() } required init?(coder: NSCoder) { super.init(coder: coder) setFont() + observeTraitChanges() } private func setFont() { titleLabel?.applySubheadlineStyle() } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - if previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory { - setFont() + private func observeTraitChanges() { + registerForTraitChanges([UITraitPreferredContentSizeCategory.self]) { (self: Self, _) in + self.setFont() } } } diff --git a/WooCommerce/Classes/ViewRelated/MainTabBarController.swift b/WooCommerce/Classes/ViewRelated/MainTabBarController.swift index bc50d8ec7c1..790ee614308 100644 --- a/WooCommerce/Classes/ViewRelated/MainTabBarController.swift +++ b/WooCommerce/Classes/ViewRelated/MainTabBarController.swift @@ -231,6 +231,13 @@ final class MainTabBarController: UITabBarController { startListeningToHubMenuTabBadgeUpdates() fixTabBarTraitCollectionOnIpadForiOS18() + observeTraitChanges() + } + + private func observeTraitChanges() { + registerForTraitChanges([UITraitHorizontalSizeClass.self, UITraitVerticalSizeClass.self]) { (self: Self, _) in + self.fixTabBarTraitCollectionOnIpadForiOS18() + } } override func viewWillAppear(_ animated: Bool) { @@ -310,12 +317,6 @@ final class MainTabBarController: UITabBarController { // MARK: - iPadOS 18 tabs support - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - fixTabBarTraitCollectionOnIpadForiOS18() - } - - /// Force a previous bottom tab bar design on iPadOS 18 when built with Xcode 16 /// /// Override a trait collection for the tab bar controller to be compact to show the same tab layout as on iPhone diff --git a/WooCommerce/Classes/ViewRelated/Survey/SurveySubmittedViewController.swift b/WooCommerce/Classes/ViewRelated/Survey/SurveySubmittedViewController.swift index e89d977dbdf..10700270326 100644 --- a/WooCommerce/Classes/ViewRelated/Survey/SurveySubmittedViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Survey/SurveySubmittedViewController.swift @@ -67,6 +67,13 @@ final class SurveySubmittedViewController: UIViewController, SurveySubmittedView applyStyleToComponents() applyLocalizedTextsToComponents() configureStackViewsAxis() + observeTraitChanges() + } + + private func observeTraitChanges() { + registerForTraitChanges([UITraitPreferredContentSizeCategory.self]) { (self: Self, _) in + self.configureStackViewsAxis() + } } @IBAction private func contactUsButtonTapped(_ sender: Any) { @@ -118,15 +125,6 @@ private extension SurveySubmittedViewController { } } -// MARK: Accessibility handling -// -extension SurveySubmittedViewController { - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - configureStackViewsAxis() - } -} - // MARK: Constants // private extension SurveySubmittedViewController { diff --git a/WooCommerce/Classes/ViewRelated/Top Banner/TopBannerView.swift b/WooCommerce/Classes/ViewRelated/Top Banner/TopBannerView.swift index 21d0a8dfb3c..c9f6867ea84 100644 --- a/WooCommerce/Classes/ViewRelated/Top Banner/TopBannerView.swift +++ b/WooCommerce/Classes/ViewRelated/Top Banner/TopBannerView.swift @@ -74,6 +74,13 @@ final class TopBannerView: UIView { actionButtons = viewModel.actionButtons.map { _ in UIButton() } super.init(frame: .zero) configureSubviews(with: viewModel) + observeTraitChanges() + } + + private func observeTraitChanges() { + registerForTraitChanges([UITraitPreferredContentSizeCategory.self]) { (self: Self, _) in + self.updateStackViewsAxis() + } } required init?(coder aDecoder: NSCoder) { @@ -312,15 +319,6 @@ private extension TopBannerView { } } -// MARK: Accessibility Handling -// -extension TopBannerView { - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - updateStackViewsAxis() - } -} - // MARK: UI Updates // private extension TopBannerView { From de08354b6a4fde101e7242e2b5558cf2f753c510 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Fri, 5 Dec 2025 09:55:08 +0700 Subject: [PATCH 3/3] uses separate trait registrations --- .../Navigation Exceptions/RoleErrorViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift b/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift index 4de99b30262..f51085d429a 100644 --- a/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift +++ b/WooCommerce/Classes/Authentication/Navigation Exceptions/RoleErrorViewController.swift @@ -102,11 +102,13 @@ class RoleErrorViewController: UIViewController { } private func observeTraitChanges() { - registerForTraitChanges([UITraitVerticalSizeClass.self, UITraitUserInterfaceStyle.self]) { (self: Self, _) in - // Hide image in compact height sizes (e.g. landscape iPhones) - // With limited space, text description should have higher priority + // Image visibility depends on vertical size class only + registerForTraitChanges([UITraitVerticalSizeClass.self]) { (self: Self, _) in self.imageView.isHidden = self.traitCollection.verticalSizeClass == .compact + } + // Appearance updates only when dark/light mode changes + registerForTraitChanges([UITraitUserInterfaceStyle.self]) { (self: Self, _) in self.updateViewAppearances() } }