SwiftUI ObjectBinding не получит обновление didchange от привязываемого объекта с помощью комбайна

Я тестирую платформу Combine и использую BindableObject в качестве концентратора уведомлений для передачи данных между несколькими представлениями в SwiftUI ContentView.

Одним из представлений является таблица. Я нажимаю на строку, и значение определяется в контрольной точке печати, поэтому объект bindable получает обновление.

Проблема в том, что новая строка не передается принимающей стороне в 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>) {
        //
    }


Он компилирует, запускает и печатает изменения, но обновление PrimaryButton в MyMiniView не происходит.

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 называется, desiredStringContentView) будет обновлен до "Hello world!!", так же, как отображаемый текст (Text(desiredString.newstring)).

Другие вопросы по тегам