From f89391b8639ca002b06f2c4fff1ffe2e1e7a0ce0 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 6 Jul 2022 13:20:24 +0300 Subject: [PATCH 1/3] Fix the safe area insets issue without method swizzling --- .../SwiftUI/VectorHostingController.swift | 117 ++++-------------- 1 file changed, 24 insertions(+), 93 deletions(-) diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift index c59dc357f..7a6ccaf7a 100644 --- a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift +++ b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift @@ -40,25 +40,20 @@ class VectorHostingController: UIHostingController { var enableNavigationBarScrollEdgeAppearance = false /// When non-nil, the style will be applied to the status bar. var statusBarStyle: UIStatusBarStyle? - - /// Whether to force-set the hosting view's safe area insets to zero. Useful when the view is used as part of a table view. - var forceZeroSafeAreaInsets: Bool { - get { - self.view.forceZeroSafeAreaInsets - } - set { - self.view.forceZeroSafeAreaInsets = newValue - } - } + + private let forceZeroSafeAreaInsets: Bool override var preferredStatusBarStyle: UIStatusBarStyle { statusBarStyle ?? super.preferredStatusBarStyle } - - init(rootView: Content) where Content: View { + /// Initializer + /// - Parameter rootView: Root view for the controller. + /// - Parameter forceZeroSafeAreaInsets: Whether to force-set the hosting view's safe area insets to zero. Useful when the view is used as part of a table view. + init(rootView: Content, + forceZeroSafeAreaInsets: Bool = false) where Content: View { self.theme = ThemeService.shared().theme + self.forceZeroSafeAreaInsets = forceZeroSafeAreaInsets super.init(rootView: AnyView(rootView.vectorContent())) - self.view.swizzleSafeAreaMethodsIfNeeded() } required init?(coder aDecoder: NSCoder) { @@ -106,6 +101,22 @@ class VectorHostingController: UIHostingController { self.view.invalidateIntrinsicContentSize() } } + + override func viewSafeAreaInsetsDidChange() { + super.viewSafeAreaInsetsDidChange() + + guard forceZeroSafeAreaInsets else { + return + } + + let counterSafeAreaInsets = UIEdgeInsets(top: -view.safeAreaInsets.top, + left: -view.safeAreaInsets.left, + bottom: -view.safeAreaInsets.bottom, + right: -view.safeAreaInsets.right) + if additionalSafeAreaInsets != counterSafeAreaInsets, counterSafeAreaInsets != .zero { + additionalSafeAreaInsets = counterSafeAreaInsets + } + } private func registerThemeServiceDidChangeThemeNotification() { NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) @@ -121,83 +132,3 @@ class VectorHostingController: UIHostingController { } } } - -// Hack for forcing zero safe area insets on hosting views. This problem occurs when the hosting view is embedded -// in a table view. See https://stackoverflow.com/questions/61552497 for further info. - -private var hasSwizzledSafeAreaMethods = false -private var forceZeroSafeAreaInsetsKey: Void? - -private extension UIView { - - var forceZeroSafeAreaInsets: Bool { - get { - return objc_getAssociatedObject(self, &forceZeroSafeAreaInsetsKey) as? Bool == true - } - set { - objc_setAssociatedObject(self, &forceZeroSafeAreaInsetsKey, newValue, .OBJC_ASSOCIATION_RETAIN) - } - } - - @objc private var _safeAreaInsets: UIEdgeInsets { - return forceZeroSafeAreaInsets ? .zero : self._safeAreaInsets - } - - @objc private var _safeAreaLayoutGuide: UILayoutGuide? { - return forceZeroSafeAreaInsets ? nil : self._safeAreaLayoutGuide - } - - func swizzleSafeAreaMethodsIfNeeded() { - guard !hasSwizzledSafeAreaMethods else { - return - } - hasSwizzledSafeAreaMethods = true - - guard let getSafeAreaInsets = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView.safeAreaInsets)) else { - return - } - - guard let _getSafeAreaInsets = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView._safeAreaInsets)) else { - return - } - - let getSafeAreaInsetsImplementation = method_getImplementation(getSafeAreaInsets) - let _getSafeAreaInsetsImplementation = method_getImplementation(_getSafeAreaInsets) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView.safeAreaInsets), - _getSafeAreaInsetsImplementation, - method_getTypeEncoding(getSafeAreaInsets)) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView._safeAreaInsets), - getSafeAreaInsetsImplementation, - method_getTypeEncoding(_getSafeAreaInsets)) - - guard let getSafeAreaLayoutGuide = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView.safeAreaLayoutGuide)) else { - return - } - - guard let _getSafeAreaLayoutGuide = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView._safeAreaLayoutGuide)) else { - return - } - - let getSafeAreaLayoutGuideImplementation = method_getImplementation(getSafeAreaLayoutGuide) - let _getSafeAreaLayoutGuideImplementation = method_getImplementation(_getSafeAreaLayoutGuide) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView.safeAreaLayoutGuide), - _getSafeAreaLayoutGuideImplementation, - method_getTypeEncoding(getSafeAreaLayoutGuide)) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView._safeAreaLayoutGuide), - getSafeAreaLayoutGuideImplementation, - method_getTypeEncoding(_getSafeAreaLayoutGuide)) - } - -} From e3506980d753e0c5e7a83cadb152f50580097915 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 6 Jul 2022 13:20:41 +0300 Subject: [PATCH 2/3] Update UIHostingController initializer --- .../TimelinePoll/Coordinator/TimelinePollCoordinator.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index a92a959cb..2ac5741c0 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -86,9 +86,8 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel } func toPresentable() -> UIViewController { - let controller = VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context)) - controller.forceZeroSafeAreaInsets = true - return controller + return VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context), + forceZeroSafeAreaInsets: true) } func canEndPoll() -> Bool { From fc0101098017c3a1bf384fef64089274d94666db Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 6 Jul 2022 13:35:59 +0300 Subject: [PATCH 3/3] Add changelog --- changelog.d/pr-6381.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-6381.bugfix diff --git a/changelog.d/pr-6381.bugfix b/changelog.d/pr-6381.bugfix new file mode 100644 index 000000000..f34e363c1 --- /dev/null +++ b/changelog.d/pr-6381.bugfix @@ -0,0 +1 @@ +VectorHostingController: Fix infinite loop due to the safe area insets fix.