SwiftUI ObjectBinding 不会使用 combine 接收来自可绑定对象的 didchange 更新
Posted
技术标签:
【中文标题】SwiftUI ObjectBinding 不会使用 combine 接收来自可绑定对象的 didchange 更新【英文标题】:SwiftUI ObjectBinding won't receive didchange update from bindable object using combine 【发布时间】:2019-07-16 10:11:35 【问题描述】:我正在测试 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】:我找不到您在哪里使用 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:"--")
要解决这个问题,您需要找到一种方法将在您的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)
带有键路径的
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)
)一样。
【讨论】:
谢谢@rraphael。我可以看到你暴露的问题。我添加了 UIViewControllerRepresentable 代码,让 AView 栩栩如生。 UIView 被包装了,我不知道如何传递初始化的 ObjectBinding @jpelayo 找到了一种将ObjectBinding
传递给包装好的UIViewController
的方法。请参阅答案的“编辑”部分。以上是关于SwiftUI ObjectBinding 不会使用 combine 接收来自可绑定对象的 didchange 更新的主要内容,如果未能解决你的问题,请参考以下文章
可以直接使用 Publisher 作为 SwiftUI 中的 @ObjectBinding 属性吗?
结合 SwiftUI 远程获取数据 - ObjectBinding 不更新视图
苹果应该通过视图传递@ObjectBinding 是啥意思?