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

时间:2019-07-16 10:11:35

标签: swift swiftui combine

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

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

问题是,新字符串不会在ContentView上广播到接收端。

我是这个新手。

具有表视图.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不会发生更新。

1 个答案:

答案 0 :(得分:1)

我找不到您在哪里使用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:"--")
}

要解决此问题,您需要找到一种方法,将在NewestString内部创建的ContentView实例“转发”到视图控制器。

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

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

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

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

具有关键路径的subscript将产生给定属性的Binding

     
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 !!”,就像显示的文本({{1} }。