dre*_*kka 3 swift swiftui combine
下面是一个说明问题的playground的内容。基本上,我有一个值存储在属性包装器UserDefaults中包装的变量中并由该变量访问@AppStorage。这让我可以访问 a 中的更新值,View但我正在寻找一种方法来侦听ViewModels 和其他非View类型中的属性的更改。
我让它在下面的代码中工作,但我不确定这是最好的方法,我希望避免PassthroughSubject为我想观看的每个属性声明 a 。
注意:我最初做了'ssink属性,但这将反映对象的任何更改,我想做一些更细粒度的事情。ObservableObjectobjectWillChange
那么有人对如何改进这项技术有任何想法吗?
import Combine
import PlaygroundSupport
import SwiftUI
class AppSettings: ObservableObject {
var myValueChanged = PassthroughSubject<Int, Never>()
@AppStorage("MyValue") var myValue = 0 {
didSet { myValueChanged.send(myValue) }
}
}
struct ContentView: View {
@ObservedObject var settings: AppSettings
@ObservedObject var viewModel: ValueViewModel
init() {
let settings = AppSettings()
self.settings = settings
viewModel = ValueViewModel(settings: settings)
}
var body: some View {
ValueView(viewModel)
.environmentObject(settings)
}
}
class ValueViewModel: ObservableObject {
@ObservedObject private var settings: AppSettings
@Published var title: String = ""
private var cancellable: AnyCancellable?
init(settings: AppSettings) {
self.settings = settings
title = "Hello \(settings.myValue)"
// Is there a nicer way to do this?????
cancellable = settings.myValueChanged.sink {
print("object changed")
self.title = "Hello \($0)"
}
}
}
struct ValueView: View {
@EnvironmentObject private var settings: AppSettings
@ObservedObject private var viewModel: ValueViewModel
init(_ viewModel: ValueViewModel) {
self.viewModel = viewModel
}
var body: some View {
Text("This is my \(viewModel.title) value: \(settings.myValue)")
.frame(width: 300.0)
Button("+1") {
settings.myValue += 1
}
}
}
PlaygroundPage.current.setLiveView(ContentView())
Run Code Online (Sandbox Code Playgroud)
绑定视图时,接受的解决方案PublishingAppStorage类不能完全工作
@propertyWrapper
public struct PublishedAppStorage<Value> {
// Based on: https://github.com/OpenCombine/OpenCombine/blob/master/Sources/OpenCombine/Published.swift
@AppStorage
private var storedValue: Value
private var publisher: Publisher?
internal var objectWillChange: ObservableObjectPublisher?
/// A publisher for properties marked with the `@Published` attribute.
public struct Publisher: Combine.Publisher {
public typealias Output = Value
public typealias Failure = Never
public func receive<Downstream: Subscriber>(subscriber: Downstream)
where Downstream.Input == Value, Downstream.Failure == Never
{
subject.subscribe(subscriber)
}
fileprivate let subject: Combine.CurrentValueSubject<Value, Never>
fileprivate init(_ output: Output) {
subject = .init(output)
}
}
public var projectedValue: Publisher {
mutating get {
if let publisher = publisher {
return publisher
}
let publisher = Publisher(storedValue)
self.publisher = publisher
return publisher
}
}
@available(*, unavailable, message: """
@Published is only available on properties of classes
""")
public var wrappedValue: Value {
get { fatalError() }
set { fatalError() }
}
public static subscript<EnclosingSelf: ObservableObject>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, PublishedAppStorage<Value>>
) -> Value {
get {
return object[keyPath: storageKeyPath].storedValue
}
set {
// /sf/answers/4134732381/
(object.objectWillChange as? ObservableObjectPublisher)?.send()
object[keyPath: storageKeyPath].publisher?.subject.send(newValue)
object[keyPath: storageKeyPath].storedValue = newValue
}
}
// MARK: - Initializers
// RawRepresentable
init(wrappedValue: Value, _ key: String, store: UserDefaults? = nil) where Value : RawRepresentable, Value.RawValue == String {
self._storedValue = AppStorage(wrappedValue: wrappedValue, key, store: store)
}
// String
init(wrappedValue: String, _ key: String, store: UserDefaults? = nil) where Value == String {
self._storedValue = AppStorage(wrappedValue: wrappedValue, key, store: store)
}
// Data
init(wrappedValue: Data, _ key: String, store: UserDefaults? = nil) where Value == Data {
self._storedValue = AppStorage(wrappedValue: wrappedValue, key, store: store)
}
// Int
init(wrappedValue: Value, _ key: String, store: UserDefaults? = nil) where Value: RawRepresentable, Value.RawValue == Int {
self._storedValue = AppStorage(wrappedValue: wrappedValue, key, store: store)
}
// URL
init(wrappedValue: URL, _ key: String, store: UserDefaults? = nil) where Value == URL {
self._storedValue = AppStorage(wrappedValue: wrappedValue, key, store: store)
}
// Double
init(wrappedValue: Double, _ key: String, store: UserDefaults? = nil) where Value == Double {
self._storedValue = AppStorage(wrappedValue: wrappedValue, key, store: store)
}
// Bool
init(wrappedValue: Bool, _ key: String, store: UserDefaults? = nil) where Value == Bool {
self._storedValue = AppStorage(wrappedValue: wrappedValue, key, store: store)
}
}
Run Code Online (Sandbox Code Playgroud)
这个的工作原理就像你正在使用一样@AppStorage
viewModel.$fade.sink { [weak self] newFadeSetting in
}
Run Code Online (Sandbox Code Playgroud)
哪里fade
@PublishedAppStorage("fade") var fade: ScreenFadeSettingItem = .on
对于绑定,你可以简单地使用
SettingSegmentPicker<ScreenFadeSettingItem>(
titles: ScreenFadeSettingItem.allCases,
selection: $viewModel.fade
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3570 次 |
| 最近记录: |