Published on

SwiftUI-da o'lchamini o'zgartirib bo'ladigan Sheet yaratish (iOS 16+)

Authors

Bu iOS 16 da chiqqan eng yaxshi yangiliklar ichida birinchi o'rinda turadi. Avval sheet faqat ekranning yuqorisiga qadar ochilardi — yarim ekran, chorak ekran sheet yaratish mumkin emas edi. iOS 16 dan boshlab bu butunlay o'zgardi.

Eng yaxshi tomoni — mavjud sheet kodi o'zgarmaydi. Shunchaki bitta yangi modifier qo'shiladi va sheet istalgan o'lchamga ega bo'ladi.


Boshlang'ich sozlama

struct ResizableSheetBootcamp: View {

    @State private var showSheet: Bool = false

    var body: some View {
        Button("Bosing") {
            showSheet.toggle()
        }
        .sheet(isPresented: $showSheet) {
            MyNextView()
        }
    }
}

struct MyNextView: View {
    var body: some View {
        Color.red
            .ignoresSafeArea()
    }
}

Standart sheet ekranning taxminan 90% ini qoplaydi. iOS 16 gacha yarim ekran sheet yaratish uchun murakkab drag gesture-larni qo'lda kodlash kerak edi.


presentationDetents — sheet o'lchamini belgilash

.sheet(isPresented: $showSheet) {
    MyNextView()
        .presentationDetents([.medium])  // yarim ekran
}

Detent — sheet keta oladigan o'lcham nuqtasi.

Tayyor detent-lar

// Katta — standart sheet o'lchami (90% ekran)
.presentationDetents([.large])

// O'rta — aynan 50% ekran
.presentationDetents([.medium])

Bir nechta detent — drag bilan o'tish

Bir nechta detent qo'shilsa, foydalanuvchi sheet-ni ular orasida sudrab o'tkaza oladi:

.sheet(isPresented: $showSheet) {
    MyNextView()
        .presentationDetents([.medium, .large])
        // Foydalanuvchi medium va large orasida sudrab o'tkazishi mumkin
}

Bir nechta detent bo'lganda avtomatik drag indikatori (kichik chiziqcha) paydo bo'ladi. Uni yashirish mumkin:

.presentationDetents([.medium, .large])
.presentationDragIndicatorHidden(true)   // indikatorni yashirish
// Lekin drag imkon bo'lsa ko'rsatib qo'yish yaxshiroq

Maxsus o'lchamlar — fraction va height

fraction — ekran ulushi

.presentationDetents([.fraction(0.1)])   // 10% — ekran pastida kichik panel
.presentationDetents([.fraction(0.5)])   // 50% — medium ga o'xshash
.presentationDetents([.fraction(0.75)])  // 75%
.presentationDetents([.fraction(0.9)])   // 90% — large ga o'xshash

height — aniq piksel

.presentationDetents([.height(200)])   // 200 piksel
.presentationDetents([.height(500)])   // 500 piksel
.presentationDetents([.height(800)])   // 800 piksel

Eslatma: height ishlatganda turli qurilmalarda ekran balandligi farq qilishini yodda tuting. fraction ko'proq xavfsiz — har qanday qurilmada to'g'ri ko'rinadi.


Bir nechta maxsus detent-larni birlashtirish

.sheet(isPresented: $showSheet) {
    MyNextView()
        .presentationDetents([
            .fraction(0.1),   // 10% — kichik pastki panel
            .medium,           // 50% — o'rta
            .height(600),      // 600 piksel
            .large             // to'liq
        ])
}

Foydalanuvchi bu to'rtta nuqta orasida sheet-ni sudrab o'tkaza oladi. Ilova ochilganda kichik panel ko'rinadi, foydalanuvchi xohlagancha kattalashtirishi mumkin.


interactiveDismissDisabled — yopishni bloklash

Foydalanuvchi sheet-ni pastga sudrab yopa olmasligi uchun:

.sheet(isPresented: $showSheet) {
    MyNextView()
        .presentationDetents([.medium])
        .interactiveDismissDisabled(true)  // yopib bo'lmaydi
}

Qachon kerak:

  • To'lov ekrani — foydalanuvchi to'lovni amalga oshirmasdan yopa olmasin
  • Onboarding — barcha qadamlar bajarilmaguncha yopilmasin
  • Muhim ruxsat so'rovi — foydalanuvchi qaror qilishi shart

@Binding bilan programmatik boshqarish

Kod orqali sheet o'lchamini o'zgartirish — masalan, tugma bosilganda:

struct ResizableSheetBootcamp: View {

    @State private var showSheet: Bool = false
    @State private var selectedDetent: PresentationDetent = .large

    var body: some View {
        Button("Bosing") {
            showSheet.toggle()
        }
        .sheet(isPresented: $showSheet) {
            MyNextView(selectedDetent: $selectedDetent)
                .presentationDetents(
                    [.medium, .large],
                    selection: $selectedDetent  // binding
                )
        }
    }
}

struct MyNextView: View {

    @Binding var selectedDetent: PresentationDetent

    var body: some View {
        VStack(spacing: 20) {
            Button("O'rta o'lcham") {
                selectedDetent = .medium  // programmatik o'zgartirish
            }

            Button("Katta o'lcham") {
                selectedDetent = .large
            }
        }
    }
}

Muhim: Programmatik tarzda o'rnatilayotgan detent presentationDetents([...]) ichida ham ko'rsatilgan bo'lishi shart. Aks holda ishlaydi, lekin to'g'ri o'lchamga o'tmaydi.

// ❌ NOTO'G'RI — .fraction(0.5) ro'yxatda yo'q
.presentationDetents([.medium, .large], selection: $selectedDetent)
selectedDetent = .fraction(0.5)  // ishlaydi, lekin o'tmaydi

// ✅ TO'G'RI — ro'yxatda ham bor
.presentationDetents([.medium, .large, .fraction(0.5)], selection: $selectedDetent)
selectedDetent = .fraction(0.5)  // to'g'ri ishlaydi

To'liq misol

struct ResizableSheetBootcamp: View {

    @State private var showSheet: Bool = false
    @State private var selectedDetent: PresentationDetent = .large

    var body: some View {
        Button("Bosing") {
            showSheet.toggle()
        }
        .sheet(isPresented: $showSheet) {
            MyNextView(selectedDetent: $selectedDetent)
                .presentationDetents(
                    [.fraction(0.2), .medium, .height(600), .large],
                    selection: $selectedDetent
                )
                .presentationDragIndicatorHidden(false)
        }
    }
}

struct MyNextView: View {

    @Binding var selectedDetent: PresentationDetent

    var body: some View {
        VStack(spacing: 20) {
            Text("Salom dunyo!")
                .font(.headline)

            Button("20%") {
                selectedDetent = .fraction(0.2)
            }

            Button("O'rta (50%)") {
                selectedDetent = .medium
            }

            Button("600 piksel") {
                selectedDetent = .height(600)
            }

            Button("Katta (to'liq)") {
                selectedDetent = .large
            }
        }
        .padding()
    }
}

Xulosa

DetentYozilishiNatija
Katta.large~90% ekran
O'rta.medium~50% ekran
Ulush.fraction(0.3)30% ekran
Balandlik.height(300)300 piksel
Bir nechta[.medium, .large]Drag bilan o'tish

iOS 16 dan oldin yarim ekran sheet yasash uchun drag gesture-larni qo'lda kodlash kerak edi — bu juda ko'p ish edi. Endi bitta .presentationDetents([.medium]) — va tayyor. Bu iOS 16 ning eng foydali API-laridan biri.

Rahmat, men Nick, bu Swiftful Thinking va keyingi videoda ko'rishguncha!

Buy mea coffee