@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
@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
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
| 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 |
Last updated on