@State
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”@State là property wrapper để lưu trữ local state cho View.
struct ContentView: View { @State private var count = 0
var body: some View { VStack { Text("Count: \(count)") Button("Increment") { count += 1 } } }}2. Quy tắc
Phần tiêu đề “2. Quy tắc”@Statenên làprivate- Thuộc về View cụ thể
- Khi thay đổi → View rebuild
- Dùng cho simple value types
3. Ví dụ cơ bản
Phần tiêu đề “3. Ví dụ cơ bản”Toggle
Phần tiêu đề “Toggle”struct ToggleExample: View { @State private var isOn = false
var body: some View { VStack { Toggle("Enable", isOn: $isOn)
Text(isOn ? "ON" : "OFF") .foregroundColor(isOn ? .green : .red) } .padding() }}TextField
Phần tiêu đề “TextField”struct TextFieldExample: View { @State private var name = ""
var body: some View { VStack { TextField("Enter name", text: $name) .textFieldStyle(.roundedBorder)
Text("Hello, \(name)!") } .padding() }}Counter
Phần tiêu đề “Counter”struct CounterExample: View { @State private var count = 0
var body: some View { HStack { Button("-") { count -= 1 } .disabled(count <= 0)
Text("\(count)") .font(.title) .frame(width: 50)
Button("+") { count += 1 } } .buttonStyle(.bordered) }}4. State với Collections
Phần tiêu đề “4. State với Collections”struct TodoList: View { @State private var items = ["Item 1", "Item 2", "Item 3"] @State private var newItem = ""
var body: some View { VStack { HStack { TextField("New item", text: $newItem) .textFieldStyle(.roundedBorder)
Button("Add") { if !newItem.isEmpty { items.append(newItem) newItem = "" } } }
List { ForEach(items, id: \.self) { item in Text(item) } .onDelete { indexSet in items.remove(atOffsets: indexSet) } } } .padding() }}5. State với Animations
Phần tiêu đề “5. State với Animations”struct AnimationExample: View { @State private var isExpanded = false
var body: some View { VStack { Button("Toggle") { withAnimation(.spring()) { isExpanded.toggle() } }
RoundedRectangle(cornerRadius: 16) .fill(.blue) .frame( width: isExpanded ? 200 : 100, height: isExpanded ? 200 : 100 ) } }}6. UI State Management
Phần tiêu đề “6. UI State Management”struct LoginForm: View { @State private var email = "" @State private var password = "" @State private var isLoading = false @State private var showError = false @State private var errorMessage = ""
var isFormValid: Bool { email.contains("@") && password.count >= 6 }
var body: some View { VStack(spacing: 16) { TextField("Email", text: $email) .textFieldStyle(.roundedBorder) .autocapitalization(.none)
SecureField("Password", text: $password) .textFieldStyle(.roundedBorder)
Button { login() } label: { if isLoading { ProgressView() } else { Text("Login") } } .buttonStyle(.borderedProminent) .disabled(!isFormValid || isLoading) } .padding() .alert("Error", isPresented: $showError) { Button("OK") { } } message: { Text(errorMessage) } }
func login() { isLoading = true // Simulate API call DispatchQueue.main.asyncAfter(deadline: .now() + 2) { isLoading = false } }}7. $ (Binding Syntax)
Phần tiêu đề “7. $ (Binding Syntax)”$state tạo Binding cho two-way data flow.
struct ParentView: View { @State private var text = ""
var body: some View { VStack { // $text tạo Binding<String> TextField("Enter text", text: $text)
// text là String value Text(text) } }}8. Private Access
Phần tiêu đề “8. Private Access”// ✅ Correct - privatestruct GoodView: View { @State private var count = 0 var body: some View { Text("\(count)") }}
// ⚠️ Not recommended - có thể accessed từ bên ngoàistruct NotRecommended: View { @State var count = 0 // Missing private var body: some View { Text("\(count)") }}📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Aspect | Detail |
|---|---|
| Mục đích | Local state cho View |
| Scope | Chỉ trong View đó |
| Access | Nên private |
| Types | Value types (Int, String, Bool…) |
| Binding | Dùng $ để tạo |