Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix VectorHostingController infinite loop #6381

Merged
merged 3 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 24 additions & 93 deletions Riot/Modules/Common/SwiftUI/VectorHostingController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,20 @@ class VectorHostingController: UIHostingController<AnyView> {
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<Content>(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<Content>(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) {
Expand Down Expand Up @@ -106,6 +101,22 @@ class VectorHostingController: UIHostingController<AnyView> {
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)
Expand All @@ -121,83 +132,3 @@ class VectorHostingController: UIHostingController<AnyView> {
}
}
}

// 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))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions changelog.d/pr-6381.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VectorHostingController: Fix infinite loop due to the safe area insets fix.