只要使用 .map 的技巧,可以讓 Unwrap 變成完全沒有必要的存在。
struct Unwrap<Value, Content: View>: View
然後 Unwrap 就可以利用這個 closure 與 .map() 將 Value? 轉換為 Content? 。最後再透過 body 這個屬性將這個 Content? 呈現出去。
Unwrap(value, then: { /* turn value into content view */ })
Unwrap(value) { /* turn value into content view */ }
當 f: A -> B
函數用 .map() 包起來,這時 .map(f)
就會把 A?
轉換成 B?
。
let value: Int? = 1
let view = Unwrap(value) {
Text("\($0)") // Content == Text
}
let T = type(of: view) // Unwrap<Int, Text>.Type
print(T) // Unwrap<Int, Text>
print(T.Body.self) // Optional<Text>
// ⭐️ use `T.self` to reference the type object itself
T.Body.self == Text?.self // true
import SwiftUI
import PlaygroundSupport
// live view
struct ContentView: View {
@State private var isValue1Nil = false
@State private var isValue2Nil = false
var value1: Int? { isValue1Nil ? nil : 1}
var value2: Int? { isValue2Nil ? nil : 2}
var body: some View {
VStack {
VStack {
// ⭐️ use .map
value1.map {
Color.red.overlay(Text("\($0)"))
}.animation(.default)
// ⭐️ use .map
value2.map {
Color.orange.overlay(Text("\($0)"))
}.animation(.default)
}.border(Color.blue, width: 3)
// toggles
HStack(spacing: 60) {
Toggle(isOn: $isValue1Nil) {
Text("value1 is nil")
}
Toggle(isOn: $isValue2Nil) {
Text("value2 is nil")
}
} // toggles
.padding()
.overlay(Rectangle().stroke(Color.gray, style: .init(dash: [6])))
} // VStack (container)
.padding().border(Color.gray, width: 3)
}
}
PlaygroundPage.current.setLiveView(ContentView())
/*
* #todo : turn it into a modifier ?
*/
import SwiftUI
/// # 📦 Unwrap
/// unwraps a value (of type `Value`) and turns it
/// into `some View` (== `Optional<Content>`).
struct Unwrap<Value, Content: View>: View {
private let value: Value? // value to be unwrapped
private let turnValueIntoContent: (Value) -> Content
// Unwrap(_:then:)
// Unwrap(_:) { /* ... */ }
init(
_ value: Value?,
@ViewBuilder then turnValueIntoContent: @escaping (Value) -> Content
) {
self.value = value
self.turnValueIntoContent = turnValueIntoContent
}
var body: some View {
value.map(turnValueIntoContent) // Optional<Content>
}
}
// ⭐️ required: 📦 Unwrap
import SwiftUI
import PlaygroundSupport
// 📦 MyView
struct MyView: View {
@State private var isValue1Nil = false
@State private var isValue2Nil = false
var value1: Int? { isValue1Nil ? nil : 1}
var value2: Int? { isValue2Nil ? nil : 2}
var body: some View {
VStack {
// stack of `Unwrap`s
VStack {
Unwrap(value1) { // 📦 Unwrap
Color.red.overlay(Text("\($0)"))
}
Unwrap(value2) {
Color.orange.overlay(Text("\($0)"))
}
}.border(Color.blue, width: 3)
// toggles
HStack {
Toggle(isOn: $isValue1Nil) {
Text("value1 is nil")
}
Toggle(isOn: $isValue2Nil) {
Text("value2 is nil")
}
Spacer()
}
.padding()
.overlay(Rectangle().stroke(Color.gray, style: .init(dash: [6])))
} // VStack (container)
.padding()
.border(Color.gray, width: 3)
}
}
struct ContentView: View {
var body: some View {
MyView() // ⭐️ required: 📦 MyView
}
}
PlaygroundPage.current.setLiveView(ContentView())