SwiftUI ObjectBinding 不会使用 combine 从可绑定对象接收 didchange 更新

SwiftUI ObjectBinding won't receive didchange update from bindable object using combine

我正在测试 Combine 框架并使用 BindableObject 作为通知中心,以便在 SwiftUI ContentView 的多个视图之间传递数据。

其中一个视图是 table。我单击一行,在打印检查点中检测到该值,因此可绑定对象收到更新。

问题是,新字符串没有广播到ContentView 的接收端。

我是新手。

具有 table 视图的视图控制器。swift(广播员):

import SwiftUI
import Combine

final public class NewestString: BindableObject {

    public var didChange = PassthroughSubject<NewestString, Never>()

    var newstring: String {
        didSet {
            didChange.send(self)
            print("Newstring: \(newstring)") //<-- Change detected
        }
    }

    init(newstring: String) {
        self.newstring = newstring
    }

    public func update() {
        didChange.send(self)
        print("--Newstring: \(newstring)")

    }

}



final class AViewController: UIViewController {

    @IBOutlet weak var someTableView: UITableView!
    var returnData = NewestString(newstring:"--")

    override func viewDidLoad() {
        super.viewDidLoad()

    }

}

/// [.....] More extensions here

extension AViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let completion = someResults[indexPath.row]

    //// [......] More code here

        self.returnData.newstring = "Test string" //<--- change caused 
        }
    }
}

主要内容视图(广播目的地):

import SwiftUI
import Combine

struct PrimaryButton: View {
    var title: String = "DefaultTitle"
    var body: some View {
        Button(action: { print("tapped") }) {
            Text(title)
        }
   }

}


struct MyMiniView: View {
    @State var aTitle: String = "InitialView"
    var body: some View {
        VStack{
            PrimaryButton(title: aTitle)
            }
    }
}


struct ContentView: View {

    @State private var selection = 0
    @ObjectBinding var desiredString: NewestString = NewestString(newstring: "Elegir destino") // <-- Expected receiver

    var body: some View {

        TabbedView(selection: $selection){

            ZStack() {

                MyMiniView(aTitle: self.desiredString.newstring ?? "--")
               // expected end use of the change, that never happens
[...]
}

struct AView: UIViewControllerRepresentable {

    typealias UIViewControllerType = AViewController


    func makeUIViewController(context: UIViewControllerRepresentableContext<AView>) -> AViewController {


        return UIStoryboard(name: "MyStoryboard", bundle: nil).instantiateViewController(identifier: String(describing: AViewController.self)) as! AViewController

    }

    func updateUIViewController(_ uiViewController: AViewController, context: UIViewControllerRepresentableContext<AView>) {
        //
    }


它编译、运行并打印更改,但 MyMiniView 的 PrimaryButton 没有更新。

我找不到您在哪里使用 AViewController 的实例,但问题在于您正在使用可绑定对象的多个实例 NewestString.

ContentView作为NewestString的实例,每次更新都会触发视图重新加载。

struct ContentView: View {

    @State private var selection = 0

    // First instance is here
    @ObjectBinding var desiredString: NewestString = NewestString(newstring: "Elegir destino") // <-- Expected receiver
}

NewestString 的第二个实例在 AViewController 中,您实际修改了它。但是,由于它不是 NewestString 的同一个实例(实际在内容视图中声明的实例),修改它不会触发视图重新加载。

final class AViewController: UIViewController {

    @IBOutlet weak var someTableView: UITableView!
    // The second instance is here
    var returnData = NewestString(newstring:"--")
}

要解决这个问题,您需要找到一种方法来 "forward" 在您的 ContentView 中创建的 NewestString 实例到视图控制器。

编辑:找到了将 ObjectBinding 的实例传递给视图控制器的方法:

当您使用 SwiftUI 将视图添加到层​​次结构中时,您需要传递一个 Binding 要从视图控制器访问的值:

struct ContentView: View {
    @ObjectBinding var desiredString = NewestString(newstring: "Hello")

    var body: some View {
        VStack {
            AView(binding: desiredString[\.newstring])
            Text(desiredString.newstring)
        }
    }
}

The subscript with a key path will produce a Binding of the given property:

protocol BindableObject {
    subscript<T>(keyPath: ReferenceWritableKeyPath<Self, T>) -> > Binding<T> { get }
}

在视图控制器包装器 (UIViewControllerRepresentable) 中,您需要将给定的 Binding 转发给实际的视图控制器实例。

struct AView: UIViewControllerRepresentable {
    typealias UIViewControllerType = AViewController

    var binding: Binding<String>

    func makeUIViewController(context: UIViewControllerRepresentableContext<AView>) -> AViewController {
        let controller = AViewController()
        controller.stringBinding = binding // forward the binding object
        return controller
    }

    func updateUIViewController(_ uiViewController: AViewController, context: UIViewControllerRepresentableContext<AView>) {

    }
}

然后,在您的视图控制器中,您可以使用绑定来更新您的值(使用 .value 属性):

final class AViewController: UIViewController {
    var stringBinding: Binding<String>!

    override func viewDidLoad() {
        super.viewDidLoad()
        stringBinding.value = "Hello world !!"
    }
}

当视图控制器的viewDidLoad被调用时,desiredString(在ContentView)将更新为"Hello world !!",就像显示的文本(Text(desiredString.newstring)).