最近在读喵神的swiftUI与Combine编程,在swift5.1后,swiftUI可以说是极大的简化了我们搭建UI的代码,另外的Combine这种响应式编程我觉得更像是继flutter,rx等的压力的背景下产生的(swiftUI中一些概念和rxswift差不多)。内容取材自swiftUI与Combine编程和网络,目的就是为了加深记忆。emm...就是这个亚子。
在swiftUI中,我们会接触到各种@State,@Binding等属性,这种@属性在swift中称为属性包装Property Wrapper,@State,@Binding等它们都是被 @propertyWrapper修饰的struct类型。以@State为例子,在SwiftUI中State定义的关键部分如下:
@propertyWrapper public struct State<Value>: DynamicProperty{
public init(initialValue value: Value)
public var value: Value {get nonmutating set}
public var wrapperdValue: Value{get nonmutating set}
public var projectedValue: Binding<Value>{get}
}
init(initialValue:),wrappedValue 和 projectedValue构成了一个 propertyWrapper 最重要的部分。在 @State 的实际使用里:
struct ContentView: View {
@State private var brain: CalculatorBrain = .left("0") //1
var body: some View {
VStack(spacing:12) {
Spacer()
Text(brain.output) //2
CalculatorButtonPad(brain: $brain) //3
.padding(.bottom)
}
}
}
1.由于
init(initialValue value: Value)的存在,我们可以使用直接给brain赋值的写法,将一个CalculatorBrain传递给brain.我们可以为属性包装中定义的 init 方法添 加更多的参数,不过 initialValue 这个 参数名相对特殊:当它出现在 init 方法的第一个参数位置时,编译器将允许我 们在声明的时候直接为 @State var brain 进行赋值 2.在访问 brain 时,这个变量暴露出来的就是 CalculatorBrain 的行为和属性。 对 brain 进行赋值,看起来也就和普通的变量赋值没有区别。但是,实际上这 些调用都触发的是属性包装中的wrappedValue。@State 的声明,在底层将 brain 属性 “包装” 到了一个 State 中,并保留外界使用者 通过 CalculatorBrain 接口对它进行操作的可能性。 3.使用美元符号前缀 ($) 访问 brain,其实访问的是projectedValue属性。在 State 中,这个属性返回一个 Binding 类型的值,通过遵守 BindingConvertible,State 暴露了修改其内部存储的方法,这也就是为什么 传递 Binding 可以让 brain 具有引用语义的原因. #####属性包装特性给了我们一个机会,可以在一定程度上简化语言的模 板代码,并且通过 “标注” 的方式来改变特性。它与自定义 getter 和 setter 做的事情 相似,只不过功能更强大,而且不需要到处重复去写一样的代码。#####利用属性包装实现货币转换
import UIKit
class ViewController: UIViewController {
@Converter(initialValue: "100", from: "USD", to: "CNY", rate: 6.88) var usd_cny
override func viewDidLoad() {
super.viewDidLoad()
print("\(usd_cny)")
print("\($usd_cny)")
}
}
@propertyWrapper struct Converter {
let from: String
let to: String
let rate: Double
var value: Double
var wrappedValue: String {
get{"\(from) \(value)"}
set{value = Double(newValue) ?? -1}
}
var projectedValue: String {
return "\(to) \(value * rate)"
}
init(
initialValue: String,
from: String,
to: String,
rate: Double
)
{
self.rate = rate
self.value = 0
self.from = from
self.to = to
self.wrappedValue = initialValue
}
}
//输出:
//USD 100.0
//CNY 688.0
利用属性包装实现UserDefault存储(取材自网络)
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
/// wrappedValue是@propertyWrapper必须要实现的属性
/// 当操作我们要包裹的属性时 其具体set get方法实际上走的都是wrappedValue 的set get 方法。
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
struct UserDefaultsConfig {
@UserDefault("had_shown_guide_view", defaultValue: false)
static var hadShownGuideView: Bool
}
///具体的业务代码。
UserDefaultsConfig.hadShownGuideView = false
print(UserDefaultsConfig.hadShownGuideView) // false
UserDefaultsConfig.hadShownGuideView = true
print(UserDefaultsConfig.hadShownGuideView) // true