说明
简单的语法是一个声明式 UI 框架的基石,让 SwiftUI 能够像写 HTML 一样简单的关键就是以下这些 Swift 新语法特性
SwiftUI Syntax
- ResultBuilder
- ViewBuilder
- Trailing Closure
- Generic/Opaque Type
- Inline
State Syntax
- PropertyWrapper
- KeyPath
- DynamicMemberLookup
SwiftUI Syntax
ResultBuilder
Result Builder是一个自定义的类型,使用声明式语法描述绘制过程的方式来创建嵌套数据,比如链表和树。
protocol Drawable {
func draw() -> String
}
struct Line: Drawable {
func draw() -> String {
return elements.map { $0.draw() }.joined(separator: "-")
}
var elements: [Drawable]
}
struct Texts: Drawable {
func draw() -> String {
return content
}
init(_ content: String) {
self.content = content
}
var content: String
}
@resultBuilder
struct DrawingBuilder {
static func buildBlock(_ components: Drawable...) -> Drawable {
return Line(elements: components)
}
}
struct ContentView : View{
//@resultBuilder方式
var body: some View{
Text("\(makeGreeting().draw())")
}
func draw(@DrawingBuilder content: () -> Drawable ) -> Drawable {
return content()
}
func makeGreeting() -> Drawable {
let greeting = draw(content: {
Texts("Hello")
Texts("!")
}
)
return greeting
}
}
ViewBuilder
允许闭包中提供多个子视图,结合ViewBuilder和便利构造函数使代码更优美。可以在项目中通过ViewBuilder注解和便利构造函数把许多具体相同特点的View封装起来,并且分离逻辑代码和视图,提升代码的可复用性,并增强可读性。
struct childView<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
content
.background(Color.red)
.cornerRadius(5)
}
}
struct ContentView: View {
var body: some View {
childView(content: {
Text("Hello")
})
}
}
Trailing Closure
尾随闭包是闭包的一种简写方式。当一个闭包作为另一个函数的最后一个参数时,可以使用尾随闭包,省略参数名。换言之,把闭包作为函数的最后一个参数时,可以使用尾随闭包,比较方便。
Generic/Opaque Type
Generic 让调用者决定参数或者返回值的类型
Opaque Type 是让方法本身决定返回的类型
protocol Animal {
associatedtype NameType // 动物名字类型占位符,即:协议实现泛型的方式(我们可能不清楚这个动物的名字的类型是啥,所以这里需要定义一个泛型,让具体实现的动物类中自己决定)
var name: NameType { get set } // 动物的名字属性,其类型为NameType泛型
func eat(food: String)
}
struct Dog: Animal{
var name: String
func eat(food: String) {
print("\(name) 吃了 \(food)")
}
}
struct Cat: Animal{
var name: String
func eat(food: String) {
print("\(name) 吃了 \(food)")
}
}
struct Zoo{
func dog() -> some Animal {
return Dog(name: "旺财")
}
func cat() -> some Animal {
return Cat(name: "小花")
}
}
func runTest() -> Void{
let zoo = Zoo()
let dog = zoo.dog()
dog.eat(food: "骨头")
let cat = zoo.cat()
cat.eat(food: "鱼")
}
Inline
函数内联 是一种编译器优化技术,它通过使用方法的内容替换直接调用该方法,就相当于假装该方法并不存在一样,这种做法在很大程度上优化了性能
@inline(never) 声明这个函数never编译成inline的形式
@inline(__always) 声明这个函数总是编译成inline的形式
inline函数可以用 闭包 的形式实现
@inline(__always) func log(){
print("hello")
}
@inline(never) func log(){
print("hello")
}
State Syntax
PropertyWrapper
它的作用对象是属性,其主旨就是:通过property Wrapper机制,对一些类似的属性的实现代码做同一封装
@propertyWrapper // 先告诉编译器 下面这个UserDefault是一个属性包裹器
struct UserDefault<T> {
//这里的属性key 和 defaultValue 还有init方法都是实际业务中的业务代码
//我们不需要过多关注
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)
}
}
}
//封装一个UserDefault配置文件
struct UserDefaultsConfig {
//实际写法就是在UserDefault包裹器的初始化方法前加了个@
@UserDefault("name_Key", defaultValue: false)
static var nameKey: Bool
@UserDefault("password_Key", defaultValue: "unknown")
static var passwordKey: String
}
//调用
func done(){
///具体的业务代码。
UserDefaultsConfig.nameKey = true
UserDefaultsConfig.passwordKey = "123"
}
KeyPath
Swift 中可以使用 KeyPath 来获取属性值,语法为\Type.property.property.....,能使用 KVC 的方法 value(forKey:) 、value(forKeyPath:) 和 KVO 的方法 addObserver(_:forKeyPath:options:context:)
class Animal: NSObject {
@objc var name: String
init(name: String) {
self.name = name
}
}
//使用
func done(){
let llama = Animal(name: "Llama")
let nameAccessor = \Animal.name
let nameCountAccessor = \Animal.name.count
_ = llama[keyPath: nameAccessor]
// "Llama"
_ = llama[keyPath: nameCountAccessor]
//5
}
DynamicMemberLookup
动态成员查找, 简单理解其目的就是动态来获取属性的值
@dynamicMemberLookup
struct Worker {
// 必须要实现的方法。返回值类型可自定义
subscript(dynamicMember member: String) -> String {
let properties = ["name": "LiaoWorking", "city": "Shanghai"]
return properties[member, default: ""]
}
}
//使用
func done(){
let worker = Worker()
let _ = worker.name // liaoworking
}