Skip to Content

@State trong SwiftUI

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

  • @State nê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

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

struct TextFieldExample: View { @State private var name = "" var body: some View { VStack { TextField("Enter name", text: $name) .textFieldStyle(.roundedBorder) Text("Hello, \(name)!") } .padding() } }

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

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

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

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)

$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

// ✅ Correct - private struct GoodView: View { @State private var count = 0 var body: some View { Text("\(count)") } } // ⚠️ Not recommended - có thể accessed từ bên ngoài struct NotRecommended: View { @State var count = 0 // Missing private var body: some View { Text("\(count)") } }

📝 Tóm tắt

AspectDetail
Mục đíchLocal state cho View
ScopeChỉ trong View đó
AccessNên private
TypesValue types (Int, String, Bool…)
BindingDùng $ để tạo
Last updated on