我正在使用属性包装器来保存我的用户默认值。在 iOS 13 设备上,此解决方案效果很好。但是,在 iOS 11 和 iOS 12 上,这些值不会保存到用户默认值中。我读到属性包装器向后兼容,所以我不知道为什么这不适用于较旧的 iOS 版本。
这是属性包装器:
@propertyWrapper
struct UserDefaultWrapper<T: Codable> {
private let key: String
private let defaultValue: T
init(key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
guard let data = UserDefaults.standard.object(forKey: key) as? Data else {
// Return defaultValue when no data in UserDefaults
return defaultValue
}
// Convert data to the desire data type
let value = try? JSONDecoder().decode(T.self, from: data) …Run Code Online (Sandbox Code Playgroud) 我一直在阅读 SwiftUI 中的属性包装器,我看到它们做得很好,但我真正不明白的一件事是@EnvironmentObject和@ObservedObject之间的区别。
从我到目前为止所学到的,我看到@EnvironmentObject在我们的应用程序的各个地方都需要一个对象时使用,但我们不需要通过所有这些地方传递它。例如,如果我们有层次结构 A -> B -> C -> D 并且对象是在 A 处创建的,那么它会保存在环境中,以便我们可以将它直接从 A 传递到 D,如果 D 需要的话。
如果我们使用在 A 创建并需要传递给 D 的@ObservedObject,那么我们也需要通过 B 和 C。
但我仍然不知道如何决定使用哪一个。以下是我制作的 2 个示例项目:
struct ContentView2: View {
var order = Order2()
var body: some View {
VStack {
EditView2()
DisplayView2()
}
.environmentObject(order)
}
}
struct EditView2: View {
@EnvironmentObject var user: Order2
var body: some View {
HStack{
TextField("Fruit", text: $user.item)
}
}
}
struct …Run Code Online (Sandbox Code Playgroud) 我正在尝试制作一个ObservableObject具有包装UserDefaults变量的属性。
为了符合ObservableObject,我需要用 包装属性@Published。不幸的是,我不能将它应用于计算属性,因为我用于UserDefaults值。
我怎么能让它工作?我必须做什么才能实现@Published行为?
尝试实现一个自定义属性包装器,它也会以相同的方式发布其更改@Publish。例如,允许我的 SwiftUI 使用我的自定义包装器接收对我的属性的更改。
我的工作代码:
import SwiftUI
@propertyWrapper
struct MyWrapper<Value> {
var value: Value
init(wrappedValue: Value) { value = wrappedValue }
var wrappedValue: Value {
get { value }
set { value = newValue }
}
}
class MySettings: ObservableObject {
@MyWrapper
public var interval: Double = 50 {
willSet { objectWillChange.send() }
}
}
struct MyView: View {
@EnvironmentObject var settings: MySettings
var body: some View {
VStack() {
Text("\(settings.interval, specifier: "%.0f")").font(.title)
Slider(value: $settings.interval, in: 0...100, step: 10) …Run Code Online (Sandbox Code Playgroud) 以下代码重现了该错误:
import SwiftUI
struct ContentView: View {
@State private var number: Int = 5
var body: some View {
NavigationView() {
VStack(spacing: 20) {
NavigationLink(destination: SecondView(bottles: $number)) {
Text("Click me")
}
}
}
}
}
struct SecondView: View {
@Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
@State private var color: UIColor = .black
@Binding var bottles: Int
var body: some View {
Text("I have \(bottles) in my bag")
.foregroundColor(Color(color))
.navigationBarTitle(Text("Water Bottle"))
.navigationBarItems(trailing:
Button("Click") {
self.someFunction()
}
)
}
func someFunction() {
if …Run Code Online (Sandbox Code Playgroud) 假设我有一个使用 的属性包装器的非常常见的用例UserDefaults。
@propertyWrapper
struct DefaultsStorage<Value> {
private let key: String
private let storage: UserDefaults
var wrappedValue: Value? {
get {
guard let value = storage.value(forKey: key) as? Value else {
return nil
}
return value
}
nonmutating set {
storage.setValue(newValue, forKey: key)
}
}
init(key: String, storage: UserDefaults = .standard) {
self.key = key
self.storage = storage
}
}
Run Code Online (Sandbox Code Playgroud)
我现在声明一个对象,该对象将保存存储在UserDefaults.
struct UserDefaultsStorage {
@DefaultsStorage(key: "userName")
var userName: String?
}
Run Code Online (Sandbox Code Playgroud)
现在,当我想在某个地方使用它时,比如说在视图模型中,我会有这样的东西。
final class ViewModel {
func getUserName() …Run Code Online (Sandbox Code Playgroud) 如何在 SwiftUI 视图上使用Binding(get: { }, set: { })带有@Binding属性的自定义绑定。我已经成功地将此自定义绑定与@State变量一起使用,但不知道如何将其应用于@Binding子视图初始值设定项。我需要它来观察@Binding父类分配给属性的更改,以便执行带有一些副作用的代码!
我想在 SwiftUI 中有一个可选的 @ObservedObject 但我一直收到编译时错误。
Property type 'AModel?' does not match that of the 'wrappedValue' property of its wrapper type 'ObservedObject'
Run Code Online (Sandbox Code Playgroud)
这是一些最小的可重现代码。
import SwiftUI
public struct AView: View {
//MARK: View Model
//Error thrown here.
@ObservedObject var model: AModel?
//MARK: Body
public var body: some View {
Text("\(model?.value ?? 0)")
}
//MARK: Init
public init() {
}
}
class AModel: ObservableObject {
let value: Int = 0
}
Run Code Online (Sandbox Code Playgroud) 我正在尝试根据另一个值获取进度条的进度值。这两个变量(waterQuantity 和进度)都在属性包装@State中
这是我的代码:
struct CircularProgressBar: View {
@State var waterQuantity: Int
@State var progress: Float = (1 + (Float(waterQuantity)/Float(1500))) * 100
var body: some View {
ZStack {
Circle()
.foregroundColor(.white)
.padding(25)
.overlay(
Circle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
.stroke(style: StrokeStyle(lineWidth: 25
, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.blue)
.rotationEffect(Angle(degrees: 270.0))
.animation(.linear)
.padding(50)
)
VStack {
Text(String(format: "%.0i mL", 500))
.font(.largeTitle)
.bold()
.foregroundColor(Color("bgStart"))
Text("1500 mL")
.foregroundColor(.gray)
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我无法使用另一个尚未初始化的变量来初始化进度。我尝试使用 init() 和 mutating func 传递值,但这些解决方案都不适合我
下面的结构适用于基本数据类型,例如Int、String、Double等,有没有办法使其适用于枚举,这样我就不必使用 rawValues 进行手动解析?
@propertyWrapper
struct Storage<T> {
let objectName: String
let defaultValue: T
let defaults: UserDefaults
init(_ objectName: String, defaultValue: T, defaults: UserDefaults = .standard) {
self.objectName = objectName
self.defaultValue = defaultValue
self.defaults = defaults
}
var wrappedValue: T {
get { return self.defaults.object(forKey: self.objectName) as? T ?? self.defaultValue }
set { self.defaults.set(newValue, forKey: self.objectName) }
}
}
Run Code Online (Sandbox Code Playgroud)
目前我的解决方法是将对象包装在枚举的另一个 getter 和 setter 中,如下所示。
enum SomeType: String {
case foo, bar, baz
}
class Defaults …Run Code Online (Sandbox Code Playgroud)