Published on

SwiftUI'da bitta view'dan bir nechta Sheet ko'rsatish

Authors

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

YondashuvQachon mos
BindingSheet ichida qiymatni real vaqtda o'zgartirish kerak bo'lganda
Alohida sheet'larIkki-uch xil, aniq belgilangan sheet bo'lganda
item parametriKo'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".

Buy mea coffee