【问题标题】:Unable to hide the navigationBar when embedding swiftUI in UIkit在 UIkit 中嵌入 swiftUI 时无法隐藏导航栏
【发布时间】:2020-03-01 10:25:17
【问题描述】:

当将一些 swiftUI 放入 UIKit viewController 中时,我试图隐藏 navigationBar

override func viewWillAppear(_ animated: Bool) {
   super.viewWillAppear(animated)
   self.navigationController?.setNavigationBarHidden(true, animated: animated)

但它不会消失。当我拿走 swiftUI 时,它可以工作。有谁知道如何解决这个问题?

编辑:

我正在实例化这样的视图 let controller = UIHostingController(rootView:view()) 其中 view 是 swiftUI,然后像添加任何 UIKit 元素一样将其添加到 UIView() 中。

【问题讨论】:

  • 它对我有用 - 在viewWillAppear 中如果你隐藏它有效但在viewDidLoad 中无效

标签: swift uiviewcontroller uikit uinavigationbar swiftui


【解决方案1】:

不幸的是,如果你在没有 UINavigationController 的情况下制作 UIHostingViewController,则需要对框架本身进行一些调整(实际上是将其 topAnchor 减少到 48)。 看来,navigationBar 间距只显示在下一个 viewWillAppear 和子视图的布局上。

这是我用于 UIHostingViewController 的解决方案。

首先,我创建了一个函数(在我的 UIHostingViewController 内部),它将设置我的内部子视图的原点(x,y)并将约束设置为 self.view。 它有条件(不要每次都这样做,只有在出现导航栏间距时):

    private var savedView: UIView?

private func removeAdditionalTopSpacing() {
    if view.subviews.count == 0  {
        return
    }
    
    var widgetFrame = view.subviews[0].frame
    let widgetStartingPoint = widgetFrame.origin.y
    widgetFrame.origin.y = 0
    widgetFrame.origin.x = 0
    
    self.view.subviews[0].frame = widgetFrame
    self.view.subviews[1].frame = widgetFrame
    
    if widgetStartingPoint > 0 {
        self.savedView = self.view

        self.savedView?.translatesAutoresizingMaskIntoConstraints = false
        self.savedView?.widthAnchor.constraint(equalTo: self.savedView!.subviews[0].widthAnchor).isActive = true
        self.savedView?.heightAnchor.constraint(equalTo: self.savedView!.subviews[0].heightAnchor).isActive = true
        self.savedView?.centerXAnchor.constraint(equalTo: self.savedView!.subviews[0].centerXAnchor).isActive = true
        self.savedView?.centerYAnchor.constraint(equalTo: self.savedView!.subviews[0].centerYAnchor).isActive = true

        self.view = self.savedView
        self.view.setNeedsLayout()
        self.view.layoutIfNeeded()
    }
}

重要提示: 我将当前视图保存在私有变量 savedView 中的原因是因为他的存在和内存释放。这样,当 removeFromSuperView 被调用时,它就不会丢失。 UIHostingViewController.view 总是有 2 个子视图。一个用于内容,另一个用于命中范围。当导航栏间距出现时,两者都会向下移动 48 点。

有两个地方我称之为:viewDidAppear() 和 viewDidLayoutSubviews():

public override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    removeAdditionalTopSpacing()
}

public override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    removeAdditionalTopSpacing()
}

【讨论】:

    【解决方案2】:

    就我而言,我不得不使用这个 UIHostingController 子类。

    class NavigationBarHiddenUIHostingController<Content: View>: UIHostingController<Content> {
      override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        if navigationController?.isNavigationBarHidden == false {
          navigationController?.isNavigationBarHidden = true
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      没有什么对我有用,所以我添加了一个观察者以在父视图中隐藏 navigationBar

          private var observer: NSKeyValueObservation?
      
      
          override func viewDidLoad() {
              super.viewDidLoad()
      
              observer = navigationController?.observe(
                  \.navigationBar.isHidden,
                  options: [.new]
              ) { [weak self] _, change in
                  guard change.newValue == false else { return }
                  self?.navigationController?.navigationBar.isHidden = true
              }
          }
      

      【讨论】:

        【解决方案4】:

        昨天也遇到了这个问题。

        我正在展示一个模态 UINavigationController 和一个 UIViewController 作为 rootViewController,它通过 UIHostingController 嵌入一个 SwiftUI 视图。

        在嵌入 SwiftUI 视图后,viewDidAppear 中的 viewDidAppear 中的常用 setNavigationBarHidden 将停止工作。

        概述:

        Root ViewController: setNavigationBarHidden in viewWillAppear
        
        Navigation Bar Visible:
        UINavigationController > root UIViewController > embedded UIHostingController
        
        Navigation Bar Invisible:
        UINavigationController > root UIViewController > no UIHostingController
        

        经过一些调试,我意识到UIHostingController 本身又调用了setNavigationBarHidden

        所以这个问题的原因是,UIHostingControllers 改变了周围的UINavigationControllerUINavigationBar

        一个简单的解决方法:

        在您的UIHostingController 嵌入的第一个呈现的 SwiftUI 视图中设置导航栏属性。

            var body: some View {
                MyOtherView(viewModel: self.viewModel)
                    .navigationBarHidden(true)
            }
        

        这将恢复 SwiftUI 和 UIHostingController 正在尝试应用到您周围的 UINavigationController 的调整。

        由于无法保证 SwiftUI 和 UIKit 之间的交互(它使用底层 UIKit),我建议将 setNavigationBarHidden 与此修饰符一起保留在周围的 viewDidAppear 中。

        【讨论】:

          【解决方案5】:

          UIHostingViewController 尊重 SwiftUI 视图的 navigationBarHidden 值。您可以在 SwiftUI 视图的末尾调用 .navigationBarHidden(true),也可以使用下面示例中显示的自定义 UIHostingController 子类。

          解决方案:

          import SwiftUI
          import UIKit
          
          class YourHostingController <Content>: UIHostingController<AnyView> where Content : View {
          
            public init(shouldShowNavigationBar: Bool, rootView: Content) {
                super.init(rootView: AnyView(rootView.navigationBarHidden(!shouldShowNavigationBar)))
            }
          
            @objc required dynamic init?(coder aDecoder: NSCoder) {
                fatalError("init(coder:) has not been implemented")
            }
          }
          

          使用示例:

          let hostVc = YourHostingController(shouldShowNavigationBar: false, rootView: YourSwiftUIView())
          

          【讨论】:

          • 我爱你。几个小时的工作,在 UINavigationBar 上尝试不同的东西......你救了我。非常感谢!
          【解决方案6】:

          我想在这里包含我的方法,以防有人在使用 SwiftUI 时发现它很有用。 我发现问题在于 UIHostingController 覆盖了我声明中的某些内容

          navigationController?.setNavigationBarHidden(true, animated: false)
          

          所以我刚刚创建了一个自定义 UIHostingController 并使用了 viewWillAppear(_ animated:Bool):

          class UIHostingViewControllerCustom:UIHostingController<YourView>{
            override func viewWillAppear(_ animated: Bool) {
              navigationController?.setNavigationBarHidden(true, animated: false)
            }
          }
          

          然后,当您将该 UIHostingController 添加到您的 ViewController 中时:

          let hostingController = UIHostingViewControllerCustom(rootView: YourView())
          hostingController.view.backgroundColor = .clear
          addChild(hostingController)
          hostingController.view.translatesAutoresizingMaskIntoConstraints = false
          view.addSubview(hostingController.view)
          hostingMapView.didMove(toParent: self)
          
          //Constraints
          hostingController.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
          hostingController.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
          hostingController.view.topAnchor.constraint(equalTo:  self.view.safeAreaLayoutGuide.topAnchor, constant: -view.safeAreaInsets.top).isActive = true
          hostingController.view.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -view.safeAreaInsets.bottom).isActive = true
          

          【讨论】:

            【解决方案7】:

            在 viewDidAppear 而不是 viewWillAppear 中调用 setNavigationBarHidden 时,似乎可以从扩展 UIHostingController 的类中隐藏导航栏。

            override func viewDidAppear(_ animated: Bool) {
                navigationController?.setNavigationBarHidden(true, animated: false)
                super.viewDidAppear(animated)
            }
            

            【讨论】:

              【解决方案8】:

              你知道你把 UIKit 函数放在 swiftUI 的什么地方吗?

              内部

              var body: some View {
              
              }
              

              您需要调用 ViewControllerWrapper 类,该类需要包含一些方法才能使用您的 UIKit 类。 UIViewControllerRepresentable 实现也是需要的。

              【讨论】:

              • 我想你把我的问题反过来了。我正在尝试嵌入 SwiftUI --> UIKit。
              • 看看这个也许它会帮助你找到你搜索的内容
              • 我的问题不在于将 swiftUI 添加到视图中。而是它带有一个不可移动的导航栏这一事实。
              猜你喜欢
              • 1970-01-01
              • 2020-06-24
              • 1970-01-01
              • 2021-09-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多