尝试一些示例。找到了一个项目,该项目的类是可绑定的对象,没有给出任何错误。现在Xcode 11 beta 4已经发布,我得到了错误:
类型'UserSettings'不符合协议'BindableObject'
它在错误上有一个修复按钮,当您单击它时,它会添加
typealias PublisherType = <#type#>
它希望您填写类型。
类型是什么?
class UserSettings: BindableObject {
let didChange = PassthroughSubject<Void, Never>()
var score: Int = 0 {
didSet {
didChange.send()
}
}
}
答案 0 :(得分:29)
Beta 4 Release notes说:
BindableObject协议的要求现在是willChange而不是 didChange,现在应该在对象更改之前发送 比它改变之后。此更改可以改善 更改通知。 (51580731)
您需要将代码更改为:
class UserSettings: BindableObject {
let willChange = PassthroughSubject<Void, Never>()
var score: Int = 0 {
willSet {
willChange.send()
}
}
}
答案 1 :(得分:3)
SwiftUI和Combine是在WWDC 2019上宣布的两个新框架。这两个框架在WWDC 2019上引起了很多关注,这些技术在其中的使用次数都得到了证明。
SwiftUI引入为
一种革命性的新方法,可以更快地构建更好的应用程序。
组合被描述为
用于处理随时间变化的值的统一声明框架
在初始版本和现在(2020年5月,Swift 5.2)之间,发生了一些更改。可能已经看过WWDC视频的SwiftUI和Combine的新手可能会对这两个框架如何协同工作提出一些疑问。
Combine定义了两个接口:Publisher和Subscriber。发布者将事件发送给订阅者。请参见下面的序列图。
如果您在SwiftUI中启动一个应用程序,然后添加Combine,将不会提及发布者或订阅者,这是使用Combine的两个主要参与者。在下面考虑这个非常简单的示例应用程序。
import SwiftUI
import Combine
import SwiftUI
final class ActorViewModel: ObservableObject {
var name : String
private var imageUrl : URL?
//@Published
private (set) var image : Image = Image(systemName: "photo") {
willSet {
DispatchQueue.main.async {
self.objectWillChange.send()
}
}
}
init(name: String, imageUrl: URL?) {
self.name = name
self.imageUrl = imageUrl
self.fetchImage()
}
private func fetchImage() {
guard nil != self.imageUrl,
String() != self.imageUrl!.absoluteString else { return }
let task = URLSession.shared.dataTask(with: self.imageUrl!) { (data, response, error) in
guard nil == error , nil != response, nil != data,
let uiImage = UIImage(data: data!) else { return }
self.image = Image(uiImage: uiImage)
}
task.resume()
}
}
struct ContentView: View {
@ObservedObject var actor : ActorViewModel
var body: some View {
HStack {
actor.image
.resizable()
.aspectRatio(contentMode: ContentMode.fit)
.frame(width: 60, height: 60)
Text(actor.name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let actor = ActorViewModel(name: "Mark Hammill",
imageUrl: URL(string: "https://m.media-amazon.com/images/M/MV5BOGY2MjI5MDQtOThmMC00ZGIwLWFmYjgtYWU4MzcxOGEwMGVkXkEyXkFqcGdeQXVyMzM4MjM0Nzg@._V1_.jpg"))
return ContentView(actor: actor)
}
}
通过画布进行的应用预览如下所示:
该应用程序使用列表视图显示演员的姓名和图像。只有两类要考虑:
根据下面的类图,视图引用了actor对象。
尽管此示例使用的是Combine,但这并不是立即可见的。没有提及发布者或订阅者。发生了什么事?
答案:查看类层次结构可填补缺失的空白。下面的类图说明了完整图片(单击图片可查看更多详细信息)。
咨询Apple的文档提供了以下类型的定义:
首先,考虑@ObservedObject
的含义。这是一个属性包装器。属性包装器可减少代码重复,并在声明隐藏属性存储和定义方式的属性时使用简洁的语法。在这种情况下,“观察到的对象”是观察另一个对象的属性。
换句话说,该属性是 Subscriber (来自Combine框架)。参与者(通过使用属性包装器)是订阅服务器,它订阅了 Publisher ,但是在这种情况下,发布服务器是什么?
“可观察对象”本身不是发布者,而是拥有发布者。 ActorViewModel符合ObservableObject
协议。通过这样做,它通过扩展名(框架在ObservableObject协议上提供)为发布者属性提供了名为objectWillChange
的发布者属性。此objectWillChange
属性的类型为PassthroughSubject
,它是Publisher协议的一种具体类型。传递主题具有称为send
的属性,该属性是用于将数据发送到任何订阅者的发布者方法。因此,名为“ objectWillChange”的属性是 Publisher 。
总而言之,订阅服务器是ContentView类中名为actor
的属性,而发布服务器是ActorViewModel类中名为objectWillChange
的属性。订阅者订阅到发布者需要什么? “ @ObservedObject”属性包装器本身就是一个订阅服务器,因此它必须订阅发布服务器。但是,视图如何找出发送到订阅服务器的更改?这由SwiftUI框架处理,我们从未见过。
实用知识:我们不必担心将视图订阅给发布者。另一方面,我们确实需要担心要确保发布者告诉订阅者什么时候要进行更改。从远程服务器上获取图像并将数据转换为图像对象后,我们调用objectWillChange.send()
通知View。订阅者从发布者那里收到即将发生更改的通知后,视图便失效(导致视图重绘)。
摘要 SwiftUI使用ObservedObject PropertyWrapper的方式从表面上看并没有放弃在公式中甚至存在Combine的事实。但是通过检查Observ ed Object和Observ able Object,可以揭示底层的Combine框架以及设计模式:
订户->订阅发布者->然后发布发布->订户收到的更改
参考:
答案 2 :(得分:0)
在Xcode 11.2.1中
BindableObject更改为ObservableObject。
ObjectBinding现在是ObservedObject。
didChange应该更改为objectWillChange。
列表(dataSource.pictures,id:.self){} 您现在也可以摆脱did / willChange发布者和.send代码,而只需将图片制作为@Published
其余的将自动为您生成。
例如:
import SwiftUI
import Combine
import Foundation
class RoomStore: ObservableObject {
@Published var rooms: [Room]
init(rooms: [Room]) {
self.rooms = rooms
}
}
struct ContentView: View {
@ObservedObject var store = RoomStore(rooms: [])
}
ref:https://www.reddit.com/r/swift/comments/cu8cqk/getting_the_errors_pictured_below_when_try_to/
答案 3 :(得分:-2)
为什么发送通知之前可以更改值?我很困惑。 有了didChange,一切对我而言都是合乎逻辑且清晰的