Fix the safe area insets issue without method swizzling

This commit is contained in:
ismailgulek
2022-07-06 13:20:24 +03:00
parent e2c62179a7
commit a0d8d86a1e
@@ -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) {
@@ -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)
@@ -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))
}
}