import SwiftUI
import PlaygroundSupport
// 📦 MaxWidth
public struct MaxWidth: PreferenceKey {
// default value (nil == not set)
public static let defaultValue: CGFloat? = nil
// ⭐️ choose max width
public static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
if let next = nextValue() {
value = max(next , value ?? 0)
}
}
}
// 🌅 MyTextField
// -----------------------------------------------------------
// 主功能:
// 1. 會向 `MaxWidth` (PreferenceKey) 報告自己 label 的寬度。
// 2. 設定自己 label 的寬度為 `labelWidth` (⭐️ 來自 parent view)。
// (剛開始為 nil,表示不強制設定寬度,由 label 自己決定寬度)
// 3. 只要來自 parent view 的 `labelWidth` 更新了,自己就會跟著更新。
// -----------------------------------------------------------
struct MyTextField: View {
@Binding var labelWidth: CGFloat? // label's width (⭐️ 由 parent @State 決定)
@Binding var text : String // text field's content (⭐️ 同上)
let label : String // text field's label
let placeholder: String // text field's placeholder
init(labelWidth: Binding<CGFloat?>, label: String, placeholder: String, text: Binding<String>) {
self._labelWidth = labelWidth
self._text = text
self.label = label
self.placeholder = placeholder
}
var body: some View {
HStack {
// label
Text(label)
// ⭐️ 1. report text's width to `MaxWidth` PreferenceKey
// (wait for parent to update its `maxWidth`)
.reportWidth(to: MaxWidth.self) // 🌀View + report, 📦 MaxWidth
// ⭐️ 2. set width with `labelWidth`(optional), and alignment
.frame(width: labelWidth, alignment: .trailing)
// text field
TextField(placeholder, text: $text)
.style(.rounded) // 🌀TextField + style
}
}
}
// 🌅 MyForm
struct MyForm: View {
@State var value1 = ""
@State var value2 = ""
@State var value3 = ""
// ⭐️ textfield label's width
@State private var labelWidth: CGFloat?
var body: some View {
Form {
// 🌅 MyTextField
// ⭐️ 1. report width to `MaxWidth`
// 2. set label's width to `labelWidth`
MyTextField(
labelWidth : $labelWidth,
label : "姓名",
placeholder: "請輸入您的名字",
text : $value1
)
MyTextField(
labelWidth : $labelWidth,
label : "電話",
placeholder: "0900-000-123",
text : $value2
)
MyTextField(
labelWidth : $labelWidth,
label : "電子信箱",
placeholder: "email@company.com",
text : $value3
)
}// Form
.cornerRadius(12)
// ⭐️ 3. parent view responses to preference change
// (update its `labelWidth`)
.onPreferenceChange(MaxWidth.self) { self.labelWidth = $0 }
}
}
struct ContentView: View {
var body: some View {
VStack {
MyForm() // 🌅 MyForm
Color.pink.cornerRadius(12)
}
.padding()
.background(Color.gray)
.cornerRadius(16)
.shadow(color: .black, radius: 6, x: 6, y: 6)
}
}
// live view
PlaygroundPage.current.setLiveView(ContentView())