我在UIView控制器中使用Swiftui来显示内容.在Swiftui有一个表格.填写完表格后,点击UIView控制器导航栏上的Run按钮,触发Swiftui中的函数执行.

UIView控制器代码如下:

import UIKit
import SwiftUI

class TestVC: UIViewController {
    var testView1: TestView1?
    override func viewDidLoad() {
        super.viewDidLoad()
        self.setupData()
        self.setupUI()
    }
    
    func setupData() {
    }
    
    func setupUI() {
        self.navigationController?.navigationBar.isTranslucent = false
        setupNavBar()
        
        let contentView = TestView1()
        let hostVC = UIHostingController(rootView: contentView)
        guard let hostView = hostVC.view else {
            return
        }
        view.addSubview(hostView)
        hostView.frame = self.view.frame
        self.testView1 = contentView
    }

    func setupNavBar() {
        let item1 = UIBarButtonItem(customView: self.runBtn)
        navigationItem.rightBarButtonItems = [item1]
    }
    
    lazy var runBtn: UIButton  = {
        let btn = UIButton()
        btn.setTitle("Run", for: .normal)
        btn.setTitleColor(.blue, for: .normal)
        btn.addTarget(self, action: #selector(runBtnAction), for: .touchUpInside)
        return btn
    }()
    
    
    @objc func runBtnAction() {
        self.testView1?.run2()
    }
}

SwiftUI查看器代码如下:

import SwiftUI

struct TestView1: View {
    @State private var inputContent: String = "init"
    
    var body: some View {
        return VStack(spacing: 20) {
            ScrollView(showsIndicators: false) {
                Button {
                    run1()
                } label: {
                    Text("Run")
                        .foregroundColor(.blue)
                }

                TextEditor(text: $inputContent)
                    .foregroundColor(.black)
                    .background(Color.gray.opacity(0.2))
            }
            
        }
        .foregroundColor(.black)
        .background(Color.white)
    }
    
    
    public func run1() {
        inputContent = "run test 1"
        print("run1 inputContent is \(inputContent)")
    }
    
    public func run2() {
        // inputContent should be "run test 1" but output is "init"
        print("run2 before inputContent is \(inputContent)")
        inputContent = "run test 2"
        // inputContent should be "run test 2" but output is still "init"
        print("run2 after inputContent is \(inputContent)")
    }
}

首先在SwiftUI中点击"Run"按钮,打印"run1 inputContent is run test 1" 这符合人们的预期. 然而,当在TestVC中点击"Run"按钮时,它总是打印"run2 inputContent is init ",而不是修改后的新值.

如何在UIKit中正确调用Swiftui的函数?

推荐答案

class TestVC: UIViewController {
        var testView1: TestView1?
        override func viewDidLoad() {
            super.viewDidLoad()
            self.setupData()
            self.setupUI()
        }
        
        func setupData() {
        }
        
        func setupUI() {
            self.navigationController?.navigationBar.isTranslucent = false
            setupNavBar()
            
            let contentView = TestView1(updateInstance: { instance in
                self.testView1 = instance
            })
            let hostVC = UIHostingController(rootView: contentView)
            guard let hostView = hostVC.view else {
                return
            }
            view.addSubview(hostView)
            hostView.frame = self.view.frame
        }
    
        func setupNavBar() {
            let item1 = UIBarButtonItem(customView: self.runBtn)
            navigationItem.rightBarButtonItems = [item1]
        }
        
        lazy var runBtn: UIButton  = {
            let btn = UIButton()
            btn.setTitle("Run", for: .normal)
            btn.setTitleColor(.blue, for: .normal)
            btn.addTarget(self, action: #selector(runBtnAction), for: .touchUpInside)
            return btn
        }()
        
        
        @objc func runBtnAction() {
            self.testView1?.run2()
        }
    }
    struct TestView1: View {
        @State private var inputContent: String = "init"
        
        let updateInstance: (TestView1) -> Void
        
        var body: some View {
            return VStack(spacing: 20) {
                ScrollView(showsIndicators: false) {
                    Button {
                        run1()
                    } label: {
                        Text("Run")
                            .foregroundColor(.blue)
                    }
    
                    TextEditor(text: $inputContent)
                        .foregroundColor(.black)
                        .background(Color.gray.opacity(0.2))
                }
                
            }
            .foregroundColor(.black)
            .background(Color.white)
            .onAppear {
                self.updateInstance(self)
            }
        }
        
        public func run1() {
            inputContent = "run test 1"
            print("run1 inputContent is \(inputContent)")
        }
        
        public func run2() {
            // inputContent should be "run test 1" but output is "init"
            print("run2 before inputContent is \(inputContent)")
            inputContent = "run test 2"
            // inputContent should be "run test 2" but output is "init"
            print("run2 after inputContent is \(inputContent)")
        }
    }
}

在提供的代码中,TestView1是通过UIHostingController在UIKit UIView控制器内使用的SwiftUI视图.当SwiftUI视图在UIKit视图层次中使用时,它将被视为子视图,并受到与其他UIKit子视图相同的视图生命周期事件的影响.

在代码中,当在TestVC类的setupUI()方法中初始化TestView1时,会向其传递一个在TestView1的onspecar闭包中调用的闭包updateInstance.该闭包用于使用在setupUI()内创建的TestView1实例更新TestVC的TestView1属性.

现在,当点击TestVC中的"Run"按钮调用runBtnAction方法时,它将调用testView1属性上的run2()方法.在run2()方法内部,正在修改TestView1 struct 的inputContent属性.

然而,由于inputContent是用@State属性包装器声明的,因此对它的任何修改都将在SwiftUI视图的下一个呈现周期触发UI更新.这意味着在调用run2()时,inputContent属性不会立即更新,但会在TestView1的下一个呈现周期中更新.

因此,由于此时inputContent属性尚未更新,因此可以预期print("inputContent之前的run2 is(InputContent)")和print("inputContent is(InputContent)")的输出都是"init"的行为.该属性将仅在TestView1的下一个呈现周期中更新.

Ios相关问答推荐

在iOS 17脉冲符号效果不起作用的情况下制作UIBarButtonItem动画(没有UIImageView)

在某些情况下,UIHostingController在自动调整时不能调整大小

如何让3张组合的`SF Symbol`图片围绕一个特定点旋转?

Xamarin Forms CustomPin iOS Render

UIAlertController的空子类是否可以与`appearance(whenContainedInInstancesOf:)`一起安全使用

IOS-获取本地化系统映像

NSHashTable要求任何MyCustomProtocolAnyObject都是类类型

如何在KMM项目中处理PHPickerViewController的回调?

RFID scanner 的蓝牙服务 UUID?

NSError 错误领域: "CBATTErrorDomain" 代码: 128 0x0000000282b111a0

为什么常量属性可能在 Swift 类中被初始化两次?

叠加视图不在堆栈中维护自身 - SwiftUI

在 SwiftUI 中打开 PDF

TextField 重复输入 SwiftUI

通过 tuist/XcodeProj 以编程方式将文件添加到本机目标

如何比较两个 UIImage 对象

有没有办法在 xib 文件中的视图和顶部布局指南之间添加约束?

UITableView 背景 colored颜色 在 iPad 上总是白色

在 iOS 7 上更改标签栏色调 colored颜色

在 UITableViewController 中使用 UISearchDisplayController 时断言失败