Bỏ qua để đến nội dung

@State

@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
}
}
}
}
  • @State nên là private
  • Thuộc về View cụ thể
  • Khi thay đổi → View rebuild
  • Dùng cho simple value types
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()
}
}
struct TextFieldExample: View {
@State private var name = ""
var body: some View {
VStack {
TextField("Enter name", text: $name)
.textFieldStyle(.roundedBorder)
Text("Hello, \(name)!")
}
.padding()
}
}
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)
}
}
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()
}
}
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
)
}
}
}
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
}
}
}

$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)
}
}
}
// ✅ 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)") }
}

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