swift - SwiftUI ObjectBinding 不会使用 combine 接收来自可绑定对象的 didchange 更新
问题描述
我正在测试 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 没有发生更新。
解决方案
我找不到您在哪里使用您的实例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
是 in 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
(in ContentView
)将被更新为“Hello world !!”,就像显示的文本(Text(desiredString.newstring)
)一样。
推荐阅读
- flutter - 如何跟踪视口中的顶部项目以进行列表颤动
- apache-kafka - 有什么区别:kafka、filebeats 和 logstash?
- html - 尝试设置最大高度时出现 CSS3 问题:100vh;
- javascript - 在一行中居中对齐多个跨度
- python - 如何将 sage 'modules' 导入 .py 文件以便在 Spyder 中运行代码
- reactjs - react-native [Expo]:我可以增加 PanResponder 的输入采样率吗?
- linux - 如何在shell中使用变量?
- c# - WPF MenuItem 内的对齐方式
- sql - Oracle Alter Table 使用 Execute Immediate 未按预期运行
- php - github页面的PHP包含函数