rbe*_*een 55 ios swift reactive swiftui combine
在命令式 Swift 中,通常使用计算属性来提供对数据的便捷访问,而无需复制状态。
假设我有这个类用于命令式 MVC 使用:
class ImperativeUserManager {
private(set) var currentUser: User? {
didSet {
if oldValue != currentUser {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
// Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
}
}
}
var userIsLoggedIn: Bool {
currentUser != nil
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
如果我想使用Combine 创建一个反应式等效项,例如用于SwiftUI,我可以轻松添加@Published到存储的属性以生成Publishers,但不能用于计算属性。
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
Run Code Online (Sandbox Code Playgroud)
我可以想到各种解决方法。我可以将我的计算属性存储起来并保持更新。
选项 1:使用属性观察器:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
Run Code Online (Sandbox Code Playgroud)
选项 2:Subscriber在我自己的班级中使用 a :
class ReactiveUserManager2: ObservableObject {
@Published private(set) var currentUser: User?
@Published private(set) var userIsLoggedIn: Bool = false
private var subscribers = Set<AnyCancellable>()
init() {
$currentUser
.map { $0 != nil }
.assign(to: \.userIsLoggedIn, on: self)
.store(in: &subscribers)
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
但是,这些变通方法不如计算属性那么优雅。它们复制状态,并且不会同时更新两个属性。
Publisher在Combine中将a 添加到计算属性的正确等效项是什么?
las*_*sej 31
You don't need to do anything for computed properties that are based on @Published properties. You can just use it like this:
class UserManager: ObservableObject {
@Published
var currentUser: User?
var userIsLoggedIn: Bool {
currentUser != nil
}
}
Run Code Online (Sandbox Code Playgroud)
所发生的事情@Published的属性包装currentUser是,它会调用objectWillChange.send()的ObservedObject上的变化。SwiftUI 视图并不关心@ObservedObjects 的哪些属性发生了变化,它只会重新计算视图并在必要时重绘。
工作示例:
class UserManager: ObservableObject {
@Published
var currentUser: String?
var userIsLoggedIn: Bool {
currentUser != nil
}
func logOut() {
currentUser = nil
}
func logIn() {
currentUser = "Demo"
}
}
Run Code Online (Sandbox Code Playgroud)
以及 SwiftUI 演示视图:
struct ContentView: View {
@ObservedObject
var userManager = UserManager()
var body: some View {
VStack( spacing: 50) {
if userManager.userIsLoggedIn {
Text( "Logged in")
Button(action: userManager.logOut) {
Text("Log out")
}
} else {
Text( "Logged out")
Button(action: userManager.logIn) {
Text("Log in")
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Pom*_*ule 11
创建订阅您要跟踪的属性的新发布者。
@Published var speed: Double = 88
lazy var canTimeTravel: AnyPublisher<Bool,Never> = {
$speed
.map({ $0 >= 88 })
.eraseToAnyPublisher()
}()
Run Code Online (Sandbox Code Playgroud)
然后,您将能够像您的@Published财产一样观察它。
private var subscriptions = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
sourceOfTruthObject.$canTimeTravel.sink { [weak self] (canTimeTravel) in
// Do something…
})
.store(in: &subscriptions)
}
Run Code Online (Sandbox Code Playgroud)
没有直接关系,但仍然是有用的,你可以跟踪多个属性与方法combineLatest。
@Published var threshold: Int = 60
@Published var heartData = [Int]()
/** This publisher "observes" both `threshold` and `heartData`
and derives a value from them.
It should be updated whenever one of those values changes. */
lazy var status: AnyPublisher<Status,Never> = {
$threshold
.combineLatest($heartData)
.map({ threshold, heartData in
// Computing a "status" with the two values
Status.status(heartData: heartData, threshold: threshold)
})
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}()
Run Code Online (Sandbox Code Playgroud)
小智 5
使用下游怎么样?
lazy var userIsLoggedInPublisher: AnyPublisher = $currentUser
.map{$0 != nil}
.eraseToAnyPublisher()
Run Code Online (Sandbox Code Playgroud)
这样,订阅就会从上游获取元素,然后你就可以使用sinkorassign来实现这个didSet想法。
| 归档时间: |
|
| 查看次数: |
17289 次 |
| 最近记录: |