首页 > 解决方案 > GeometryReader 仍然考虑到宿主控制器的隐藏导航栏

问题描述

我有一个UIHostingController隐藏导航栏的子类viewDidLoad。在我的主机控制器中,我有一个 SwiftUI 视图,它使用几何阅读器来计算顶部安全区域插图。不幸的是,我认为几何读者仍然认为导航栏是可见的,因此报告了错误的顶部安全区域插图大小。我如何让它报告正确的插图?这是我的代码:

final class NavigationHostingController<Content: View>: UIHostingController<Content> {
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController?.navigationBar.isHidden = true
    }
}


struct AccountDetailsView: View {
    var body: some View {
        GeometryReader { geometry in
            VStack(spacing: 0) {
                Color.yellow
                    .edgesIgnoringSafeArea(.all)
                    .frame(height: geometry.safeAreaInsets.top)
                // some other views
           }
        }
    }
}

let viewController = NavigationHostingController(rootView: accountDetailsView)
navigationController?.pushViewController(viewController, animated: true)
        

标签: iosswiftswiftui

解决方案


UIHostingController 不会忽略安全区域插图,这似乎是一个错误。有一种方法可以通过调整 UIHostingController 的安全区域方法来解决问题,这样我们就可以返回以下插图.zero

final class NavigationHostingController<Content: View>: UIHostingController<Content> {

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController?.navigationBar.isHidden = true
        disableSafeArea()
    }
    
    private func disableSafeArea() {
        guard let viewClass = object_getClass(view) else { return }
        
        let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoreSafeArea")
        if let viewSubclass = NSClassFromString(viewSubclassName) {
            object_setClass(view, viewSubclass)
        }
        else {
            guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return }
            guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return }
            
            if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) {
                let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { obj in
                    if var insets = (obj as? UIView)?.superview?.safeAreaInsets {
                        insets.top = 0
                        return insets
                    } else {
                        return .zero
                    }
                }
                class_addMethod(viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method))
            }
            
            objc_registerClassPair(viewSubclass)
            object_setClass(view, viewSubclass)
        }
    }
}

推荐阅读