Add `Defaults.Toggle` (#69)
This commit is contained in:
parent
64351c25ed
commit
63d93f97ad
|
@ -5,10 +5,11 @@ import Combine
|
|||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
extension Defaults {
|
||||
final class Observable<Value: Serializable>: ObservableObject {
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
private var observation: DefaultsObservation?
|
||||
private let key: Defaults.Key<Value>
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
var value: Value {
|
||||
get { Defaults[key] }
|
||||
set {
|
||||
|
@ -44,6 +45,8 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
|
|||
public typealias Publisher = AnyPublisher<Defaults.KeyChange<Value>, Never>
|
||||
|
||||
private let key: Defaults.Key<Value>
|
||||
|
||||
// Intentionally using `@ObservedObjected` over `@StateObject` so that the key can be dynamicaly changed.
|
||||
@ObservedObject private var observable: Defaults.Observable<Value>
|
||||
|
||||
/**
|
||||
|
@ -61,7 +64,10 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
|
|||
|
||||
var body: some View {
|
||||
Text("Has Unicorn: \(hasUnicorn)")
|
||||
Toggle("Toggle Unicorn", isOn: $hasUnicorn)
|
||||
Toggle("Toggle", isOn: $hasUnicorn)
|
||||
Button("Reset") {
|
||||
_hasUnicorn.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -110,4 +116,101 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
|
|||
key.reset()
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
|
||||
extension Defaults {
|
||||
/**
|
||||
Creates a SwiftUI `Toggle` view that is connected to a `Defaults` key with a `Bool` value.
|
||||
|
||||
The toggle works exactly like the SwiftUI `Toggle`.
|
||||
|
||||
```
|
||||
extension Defaults.Keys {
|
||||
static let showAllDayEvents = Key<Bool>("showAllDayEvents", default: false)
|
||||
}
|
||||
|
||||
struct ShowAllDayEventsSetting: View {
|
||||
var body: some View {
|
||||
Defaults.Toggle("Show All-Day Events", key: .showAllDayEvents)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also listen to changes:
|
||||
|
||||
```
|
||||
struct ShowAllDayEventsSetting: View {
|
||||
var body: some View {
|
||||
Defaults.Toggle("Show All-Day Events", key: .showAllDayEvents)
|
||||
// Note that this has to be directly attached to `Defaults.Toggle`. It's not `View#onChange()`.
|
||||
.onChange {
|
||||
print("Value", $0)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
*/
|
||||
public struct Toggle<Label, Key>: View where Label: View, Key: Defaults.Key<Bool> {
|
||||
@ViewStorage private var onChange: ((Bool) -> Void)?
|
||||
|
||||
private let label: () -> Label
|
||||
|
||||
// Intentionally using `@ObservedObjected` over `@StateObject` so that the key can be dynamicaly changed.
|
||||
@ObservedObject private var observable: Defaults.Observable<Bool>
|
||||
|
||||
public init(key: Key, @ViewBuilder label: @escaping () -> Label) {
|
||||
self.label = label
|
||||
self.observable = Defaults.Observable(key)
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
SwiftUI.Toggle(isOn: $observable.value, label: label)
|
||||
.onChange(of: observable.value) {
|
||||
onChange?($0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
|
||||
extension Defaults.Toggle where Label == Text {
|
||||
public init<S>(_ title: S, key: Defaults.Key<Bool>) where S: StringProtocol {
|
||||
self.label = { Text(title) }
|
||||
self.observable = Defaults.Observable(key)
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
|
||||
extension Defaults.Toggle {
|
||||
/// Do something when the value changes to a different value.
|
||||
public func onChange(_ action: @escaping (Bool) -> Void) -> Self {
|
||||
onChange = action
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
@propertyWrapper
|
||||
private struct ViewStorage<Value>: DynamicProperty {
|
||||
private final class ValueBox {
|
||||
var value: Value
|
||||
|
||||
init(_ value: Value) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
@State private var valueBox: ValueBox
|
||||
|
||||
var wrappedValue: Value {
|
||||
get { valueBox.value }
|
||||
nonmutating set {
|
||||
valueBox.value = newValue
|
||||
}
|
||||
}
|
||||
|
||||
init(wrappedValue value: @autoclosure @escaping () -> Value) {
|
||||
self._valueBox = .init(wrappedValue: ValueBox(value()))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
39
readme.md
39
readme.md
|
@ -153,6 +153,8 @@ Defaults[isUnicorn]
|
|||
|
||||
### SwiftUI support
|
||||
|
||||
#### `@Default`
|
||||
|
||||
You can use the `@Default` property wrapper to get/set a `Defaults` item and also have the view be updated when the value changes. This is similar to `@State`.
|
||||
|
||||
```swift
|
||||
|
@ -165,7 +167,10 @@ struct ContentView: View {
|
|||
|
||||
var body: some View {
|
||||
Text("Has Unicorn: \(hasUnicorn)")
|
||||
Toggle("Toggle Unicorn", isOn: $hasUnicorn)
|
||||
Toggle("Toggle", isOn: $hasUnicorn)
|
||||
Button("Reset") {
|
||||
_hasUnicorn.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -174,6 +179,38 @@ Note that it's `@Default`, not `@Defaults`.
|
|||
|
||||
You cannot use `@Default` in an `ObservableObject`. It's meant to be used in a `View`.
|
||||
|
||||
#### `Toggle`
|
||||
|
||||
There's also a `SwiftUI.Toggle` wrapper that makes it easier to create a toggle based on a `Defaults` key with a `Bool` value.
|
||||
|
||||
```swift
|
||||
extension Defaults.Keys {
|
||||
static let showAllDayEvents = Key<Bool>("showAllDayEvents", default: false)
|
||||
}
|
||||
|
||||
struct ShowAllDayEventsSetting: View {
|
||||
var body: some View {
|
||||
Defaults.Toggle("Show All-Day Events", key: .showAllDayEvents)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also listen to changes:
|
||||
|
||||
```swift
|
||||
struct ShowAllDayEventsSetting: View {
|
||||
var body: some View {
|
||||
Defaults.Toggle("Show All-Day Events", key: .showAllDayEvents)
|
||||
// Note that this has to be directly attached to `Defaults.Toggle`. It's not `View#onChange()`.
|
||||
.onChange {
|
||||
print("Value", $0)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Requires at least macOS 11, iOS 14, tvOS 14, watchOS 7.*
|
||||
|
||||
### Observe changes to a key
|
||||
|
||||
```swift
|
||||
|
|
Loading…
Reference in New Issue