SwiftUI 在 UIViewController 中调用委托

Posted

技术标签:

【中文标题】SwiftUI 在 UIViewController 中调用委托【英文标题】:SwiftUI calling delegate in UIViewController 【发布时间】:2021-08-24 22:32:18 【问题描述】:

这是我第一次使用 SwiftUI,我已经搜索过这个问题,但没有找到任何可行的解决方案,所以我将不胜感激。

我有一个 UIViewController,我在其中通过 UIHostingController 呈现一个 SwiftUI 视图。我想要实现的是,当我在 SwiftUI 视图中按下按钮时,该操作将触发 UIViewController 中的委托,或者,触发 UIViewController 中的一个函数,然后从该函数触发委托。

class MyViewController: UIViewController 

public var delegate: MyViewControllerDelegate?
let facialView = UIHostingController(rootView: FacialTutorial())

override func viewWillAppear(_ animated: Bool) 

addChild(facialView)
view.addSubview(facialView.view)
setupConstraints()



extension MyViewController 

@objc private func buttonPressed() 

delegate?.buttonPressed()







在我的 SwiftUI 视图中 FacialTutorial

struct FacialTutorial: View 

var body: some View 

        VStack() 
          Button 
              // I want to call delegate?.buttonPressed() action here
           label: 
              Text("Press button")
          
        
      





编辑

好吧,说得更清楚些,我的 ViewController 在很多情况下对页面进行了不同的配置。所以在实践中,我不会从 viewWillAppear 启动 SwiftUI 视图。相反,我就是这样做的

public var delegate: MyViewControllerDelegate?
let facialView = UIHostingController(rootView: FacialTutorial())


override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    
    addChild(facialView)
    configureSubviews()




private func configureForInstructionMode() 

  view.addSubview(facialUIView.view)
  setupConstraints()

我必须采用这种方式,因为我需要根据要配置的模式来不同地配置视图。当我在 viewDidLoad 或 viewWillAppear 中声明 faceView 时,我无法访问 configureForInstructionMode() 中的实例,或者它的值为 nil..

【问题讨论】:

这能回答你的问题吗? SwiftUI - Button - How to pass a function request to parent 【参考方案1】:

您只需要通过其初始化程序将对您的MyViewController 实例的引用传递给MyUIView(可能应该是MyView,因为它不是UIView 的子类)。您可以使用委托模式,也可以传递一个闭包,然后从闭包中调用委托方法。第二种方式更“Swifty”。

(我已将代码移至viewDidLoad,因为如果您在viewWillAppear 中有它,如果视图控制器出现、消失并再次出现,则视图可能会被多次添加)

class MyViewController: UIViewController 

    public var delegate: MyViewControllerDelegate? 
        
    weak var myView: UIHostingController<MyUIView>!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        let myView = UIHostingController(rootView: MyUIView(buttonHandler:  [weak self] in
                self?.delegate?.buttonPressed()
            ))
        self.myView = myView
        view.addSubview(myView.view)
        setupConstraints()
    

然后,在您的 SwiftUI 视图中,您可以声明一个属性来保存闭包并在您的按钮中调用该闭包

struct MyUIView: View 
    
    var buttonHandler: (()->Void)?
    
    var body: some View 
        VStack() 
            Button 
                buttonHandler?()
             label: 
                Text("Press button")
            
            
        
    

【讨论】:

实际上它现在工作了!谢谢您的帮助。顺便问一下,你知道我在启动时如何将一些参数传递给 SwiftUI 视图吗?这样我就可以传递我想要配置的模式,然后在我的 SwiftUI 中我可以针对特定模式调整视图? 和我传递闭包时一样——在MyUIView 中声明一个属性,然后通过初始化程序传递值。 好的,我做到了,它也有效。但是我注意到我只想在某些模式下显示 SwiftUI 视图,而在其他模式下不启动 SwiftUI 视图而只使用常规 ViewController。这就是为什么我将 SwiftUI 的启动移到特定函数中的原因,这样当特定模式为 true 时,才会启动 SwiftUI 视图。你知道怎么做吗? 您在加载视图之前设置视图控制器的相关属性,然后您可以在viewDidLoad 中测试这些属性。确切的方式取决于您如何访问此视图控制器 - segue,只是显示或呈现它。在viewWillAppear 中将视图设置为hidden 也可能是一个选项。

以上是关于SwiftUI 在 UIViewController 中调用委托的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 如何在 SwiftUI 中弹出到特定视图?

在 UIView 中创建 WKWebView

SwiftUI - SwiftUI 中是不是有等效的 popViewController?

SwiftUI - 在 SwiftUI 中对显示的 JSON 文件进行排序,分节或排序

如何在 SwiftUI 中使用 Realm

在 SwiftUI 中滑动列表时隐藏导航栏