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