- Published on
SwiftUI'da bitta view'dan bir nechta Sheet ko'rsatish
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Bitta view'dan ikki yoki undan ko'p turli sheet ko'rsatish β SwiftUI'da eng ko'p uchraydigan muammolardan biri. Ushbu videoda avval nima uchun ishlashini β shundan keyin esa uchta to'g'ri yechimni ko'rib chiqamiz.
Dastlabki tuzilma
struct RandomModel: Identifiable {
let id: String = UUID().uuidString
let title: String
}
struct MultipleSheetsBootcamp: View {
@State var selectedModel: RandomModel = RandomModel(title: "Starting Title")
@State var showSheet: Bool = false
var body: some View {
VStack(spacing: 20) {
Button("Button 1") {
selectedModel = RandomModel(title: "1")
showSheet.toggle()
}
Button("Button 2") {
selectedModel = RandomModel(title: "2")
showSheet.toggle()
}
}
.sheet(isPresented: $showSheet) {
NextScreen(selectedModel: selectedModel)
}
}
}
struct NextScreen: View {
let selectedModel: RandomModel
var body: some View {
Text(selectedModel.title)
.font(.largeTitle)
}
}
Simulyatorda sinab ko'rsak: "Button 2"ni bosganida "Starting Title" chiqadi β "2" emas. Bu noto'g'ri.
Muammoning sababi
Sheet closure'idagi content β ya'ni NextScreen(selectedModel: selectedModel) β ekran birinchi marta yuklanganida, ya'ni hali hech qanday tugma bosilmagan paytda, bir marta yaratiladi. O'sha paytda selectedModel.title β "Starting Title". Tugmani bosganimizda selectedModel yangilansa ham, sheet closure qaytadan ishga tushmaydi β u avvalgi qiymatni "eslab" qoladi.
Noto'g'ri yondashuv: closure ichida shartli mantiq
Ko'plar quyidagicha yechim topishga harakat qiladi β bu ham ishlamaydi:
@State var selectedIndex: Int = 0
// Button 1 da:
selectedIndex = 1
// Button 2 da:
selectedIndex = 2
// Sheet closure:
.sheet(isPresented: $showSheet) {
if selectedIndex == 1 {
NextScreen(selectedModel: RandomModel(title: "1"))
} else if selectedIndex == 2 {
NextScreen(selectedModel: RandomModel(title: "2"))
} else {
NextScreen(selectedModel: RandomModel(title: "Starting Title"))
}
}
Bu ham ishlamaydi β sabab bir xil: closure yuklanganda selectedIndex == 0, shuning uchun else ishga tushadi va "Starting Title" ko'rsatadi. Shartli mantiqni sheet closure ichiga qo'ymaslik kerak.
Yechim 1: Binding
NextScreendagi selectedModelni @Binding qilamiz:
struct NextScreen: View {
@Binding var selectedModel: RandomModel
var body: some View {
Text(selectedModel.title)
.font(.largeTitle)
}
}
Sheet'ga dollar belgisi bilan ulaymiz:
.sheet(isPresented: $showSheet) {
NextScreen(selectedModel: $selectedModel)
}
Endi tugmani bosganimizda selectedModel yangilansa, bu o'zgarish NextScreenga ham darhol uzatiladi β chunki bu binding (ikki tomonlama bog'lanish). Simulyatorda sinab ko'rsak: "Button 2" β "2" chiqadi.
Qachon mos: NextScreen state'ni o'zgartirishiga ruxsat berish kerak bo'lganda yoki o'zgarishni real vaqtda ko'rish kerak bo'lganda.
Cheklov: Agar NextScreen murakkabroq bo'lsa va unga statik (o'zgarmaydigan) model kerak bo'lsa β binding muammo tug'dirishi mumkin.
Yechim 2: Har bir tugmaga alohida Sheet
Har bir tugmaga o'zining mustaqil sheet modifikatorini qo'shamiz:
@State var showSheet1: Bool = false
@State var showSheet2: Bool = false
var body: some View {
VStack(spacing: 20) {
Button("Button 1") {
showSheet1.toggle()
}
.sheet(isPresented: $showSheet1) {
NextScreen(selectedModel: RandomModel(title: "1"))
}
Button("Button 2") {
showSheet2.toggle()
}
.sheet(isPresented: $showSheet2) {
NextScreen(selectedModel: RandomModel(title: "2"))
}
}
}
Bu yerda har bir sheet closure o'zining "doimiy" content'iga ega β RandomModel(title: "1") va RandomModel(title: "2") β shuning uchun ular har doim to'g'ri qiymat ko'rsatadi.
Muhim eslatma: Sheet modifikatorlarini bir-birining ichiga joylashtirib bo'lmaydi β bir xil ierarxiyada faqat bitta sheet ishlaydi. Shuning uchun ularni bir xil darajada (sibling) joylashtirish kerak β ya'ni ikkalasi ham tugmaning o'ziga yoki VStack'ning parallel elementlariga qo'yilishi kerak, biri ikkinchisining closure'i ichiga emas.
Qachon mos: Ikki yoki uch xil sheet bo'lganda qulay va toza yechim.
Cheklov: 5-10 ta turli sheet bo'lsa, shuncha @State var showSheetN yaratish zerikarli bo'ladi.
Yechim 3: item parametri bilan Sheet (eng kengaytiriladigan)
Bu yondashuv isPresented: Bool o'rniga item: Optional<Identifiable> ishlatadi. RandomModel allaqachon Identifiablega mos keladi, shuning uchun uni optional qilib qo'yamiz:
@State var selectedModel: RandomModel? = nil
Sheet:
.sheet(item: $selectedModel) { model in
NextScreen(selectedModel: model)
}
Tugmalar:
Button("Button 1") {
selectedModel = RandomModel(title: "1")
}
Button("Button 2") {
selectedModel = RandomModel(title: "2")
}
Bu yondashuv qanday ishlaydi: selectedModel nildan boshqa qiymatga o'tishi bilanoq, sheet avtomatik ravishda ochiladi va closure'ga aynan o'sha yangi qiymat uzatiladi. Sheet yopilganda esa selectedModel avtomatik nilga qaytariladi.
showSheet.toggle() yoki qo'shimcha boolean'larga hech qanday ehtiyoj yo'q.
Qachon mos: Ko'plab turli sheet kerak bo'lganda yoki ma'lumotlar to'plami katta bo'lganda β eng kengaytiriladigan yechim.
Masshtab testi β 50 ta element
ScrollView {
VStack {
ForEach(0..<50) { index in
Button("Button \(index)") {
selectedModel = RandomModel(title: "\(index)")
}
}
}
}
.sheet(item: $selectedModel) { model in
NextScreen(selectedModel: model)
}
Endi 50 ta tugmaning har biri bosilganda, aynan o'sha tugmaning qiymati bilan sheet ochiladi β birorta qo'shimcha @State o'zgaruvchisiz. Agar bu RandomModel o'rniga haqiqiy foydalanuvchi profili bo'lsa, har bir tugma bosilganda o'sha foydalanuvchining profil ekrani ochiladi.
Xulosa: uchta yechimni solishtirish
| Yondashuv | Qachon mos |
|---|---|
| Binding | Sheet ichida qiymatni real vaqtda o'zgartirish kerak bo'lganda |
| Alohida sheet'lar | Ikki-uch xil, aniq belgilangan sheet bo'lganda |
item parametri | Ko'p va dinamik sheet'lar kerak bo'lganda β eng kengaytiriladigan |
Asosiy qoida: sheet closure'i ichiga hech qachon shartli mantiq qo'ymang β content ekran yuklanganida bir marta yaratiladi va keyingi o'zgarishlarni "ko'rmaydi".