Published on

SwiftUI ilovasida Alert'larni qanday ko'rsatish kerak

Authors

Yana qaytib keldik! Men — Nik, va bugun biz alertlar haqida gaplashamiz. Alert'lar — foydalanuvchiga biror narsani aytish uchun pop-up (qalqib chiqadigan oynacha) ko'rsatishning eng oson va eng tezkor yo'li, xolos. Alert ekranning markazida paydo bo'ladi, va unda bitta xabar, so'ngra bir yoki ikki tugma bo'ladi. Odatda bu — "xatolik yuz berdi" deb yozilgan, foydalanuvchi "OK" deb bosishi mumkin bo'lgan pop-up, yoki "ushbu rasmni saqlaysizmi?" deb so'rab, foydalanuvchiga "saqlash" yoki "bekor qilish" tugmalarini taqdim etadigan oynacha kabi narsalar bo'ladi. Demak, alert odatda ilovangizdagi eng oddiy narsalardan biri, ammo bu xabarni foydalanuvchiga to'g'ridan-to'g'ri ko'rsatishning eng oson yo'li — shunday qilib, ular xabarni o'qishlari va "ha" yoki "yo'q" kabi bir tanlovni bosishlari shart bo'ladi.

Bitta, yolg'iz alert'ni amalga oshirish — bu ancha oson ish, bu, asosan, ushbu kursda allaqachon ko'rib chiqgan sheet kabi modifikatorlarga juda o'xshaydi. Ammo ilovalarda ko'pincha alert'ingizni turli vaziyatlar uchun moslashtirishni xohlaysiz — masalan, xatolik bilan muvaffaqiyat holati uchun alert xabari boshqacha bo'lishini xohlashingiz mumkin, shuningdek, foydalanuvchi alert ichida qaysi tugmalarni bosa olishini ham o'zgartirishni xohlashingiz mumkin. Shuning uchun avval sizlarga oddiy alert'ni qanday amalga oshirishni ko'rsataman, so'ngra biroz murakkabroq darajaga o'tib, bir xil view ichida dinamik yoki turlicha alert'larni ko'rsatishning bir qancha usulini ko'rib chiqamiz.


Yangi loyiha fayli yaratish

Yana Xcode loyihamizga qaytdik. Har doimgidek, shu video uchun yangi fayl yaratamiz — Navigator'da o'ng tugmani bosib, yangi fayl yaratamiz, bu SwiftUI View bo'ladi, va biz alert'lar haqida gaplashayotganimiz uchun buni AlertBootcamp deb ataymiz. "Create" tugmasini bosamiz, ichkariga kirgach esa canvas'da "Resume" tugmasini bosib, kod yozishni boshlaymiz.

Bu videoni avvalgisidan biroz qisqaroq qilishga harakat qilaman, chunki oldingisi juda uzun bo'lib ketdi. Avval view'imizga bitta tugma (Button) qo'shamiz — qavslarni ochganimizda, tugmaning sarlavhasi uchun shunchaki "Click here" deb yozamiz, harakat (action) uchun esa Enter bosib, hozircha bo'sh qoldiramiz.


Oddiy alert ko'rsatish

Alert ko'rsatish, aslida, juda sodda — shunchaki .alert deb chaqiramiz, va buni body ichidagi istalgan joyda chaqirishimiz mumkin. isPresented parametridan foydalanamiz — bu sizga, ehtimol, tanish bo'lishi kerak, chunki bu, bir necha video oldin ko'rib chiqgan .sheet modifikatorida ham aynan shunday ishlatilgan edi: u yerda ham isPresented orqali, Boolean o'zgaruvchi yordamida nazorat qilingan edi.

Shuning uchun bu yerda ham, shu alert'ni ko'rsatish uchun, biz almashtirib turishimiz mumkin bo'lgan Boolean o'zgaruvchi kerak. bodydan tashqarida, yuqorida, @State var showAlert deb e'lon qilamiz, turi Bool bo'ladi, va uni falsega tenglashtiramiz. Buni dollar belgisi bilan isPresentedga bog'laymiz: $showAlert. Tarkib (content) uchun esa bizga bir Alert qo'shishimiz kerak — agar Option tugmasini bosib alertning ustiga bossak, tarkib aslida Alert qaytaradigan funksiya bo'lishi kerakligini ko'ramiz.

Shuning uchun shu tarkibga Alert qo'shamiz, qavslarni ochsak, bizga bir qancha turli variant ko'rsatiladi. Avval shunchaki titleni ko'rsatishdan boshlaylik — title variantini tanlaymiz, va u bizdan, aytganidek, bir Text so'raydi — shu yerga, masalan, "There was an error" deb yozamiz.

Endi shu alert'ni tugmani bosganimizda ko'rsatishni xohlaymiz — shuning uchun tugmaning harakati ichida showAlert.toggle()ni chaqiramiz:

@State var showAlert: Bool = false

var body: some View {
    Button("Click here") {
        showAlert.toggle()
    }
    .alert(isPresented: $showAlert) {
        Alert(title: Text("There was an error"))
    }
}

Endi preview'da "Click here" deb yozilgan tugmamizni bossak, birinchi alert'imizni ko'ramiz — uning sarlavhasi "There was an error", va standart "OK" tugmasi bor. Mana shu — alert aynan shu, ya'ni siz ilovalarda ko'plab marta ko'rgan, iOS-ning standart pop-up oynachasi. Va biz alert nima deyishini ham, unda qanday tugmalar bo'lishini ham moslashtirishimiz mumkin.


Title, message va custom tugmalar

Faqat sarlavhaga ega bo'lgan standart alert bizga shunchaki sarlavha va "OK" tugmasini beradi, ammo biz boshqacha alert'larni ham ko'rsatishimiz mumkin. Avvalgi alert'ni izohga olib, yana bir Alert qo'shaylik — bu safar title, message, primaryButton va secondaryButton variantidan foydalanamiz. Buni o'qilishi osonroq qilish uchun, har birini o'z qatoriga joylashtiramiz.

Avval titlega bir Text qo'shamiz — masalan, "This is the title". message esa optional Text, shuning uchun agar xabar kerak bo'lmasa, buni nil qoldirib qo'yishimiz mumkin — ammo biz xabar ham qo'shmoqchimiz, shuning uchun Text("We will describe the error") deb yozamiz.

Endi alert'ga qaysi tugmalarni qo'shmoqchi ekanligimizni hal qilishimiz mumkin. Nuqta belgisini bossak, bizda bir qancha variant ko'ramiz: cancel (bekor qilish) tugmalari, default (standart) tugmalar, va destructive (buzilish/o'chirish xarakteridagi) tugmalar. Asosiy bilishingiz kerak bo'lgan narsa shu: destructive tugmalar qizil rangda, default tugmalar esa ko'k rangda bo'ladi, cancel tugmalari esa odatdagi bekor qilish tugmalariga o'xshaydi.

Hozircha, label va text'ga ega bo'lgan destructive variantini tanlaymiz, va matn sifatida, masalan, "Delete" deb yozamiz (bu yerdagi "xatolik" nimaligi unchalik muhim emas, bu shunchaki bir misol, xolos). Ikkinchi tugma uchun esa, nuqta qo'yib, standart cancel tugmasidan foydalanamiz:

.alert(isPresented: $showAlert) {
    Alert(
        title: Text("This is the title"),
        message: Text("We will describe the error"),
        primaryButton: .destructive(Text("Delete")),
        secondaryButton: .cancel()
    )
}

Endi preview'ni ko'rsak: alertning sarlavhasi, tasviri (message), va custom tugmalarimiz bor — qizil rangdagi "Delete" (destructive) tugmasi, va standart "Cancel" tugmasi. Demak, biz bu tugmalarni nafaqat ko'rinishi jihatdan, balki ular nima qilishi jihatdan ham moslashtirishimiz mumkin.

Shuning uchun, destructive uchun shu varianti o'rniga, uni o'chirib, yana nuqta bosib, label va actionga ega bo'lgan destructive variantini topamiz. Bu yerda Text("Delete")ni qaytadan yozamiz, va action uchun Enter bosamiz — bu kichik harakat bo'lagi xuddi tugma kabi ishlaydi: foydalanuvchi "Delete"ni bosganida, biz shu yerga nima yozsak, aynan shu narsa amalga oshadi.

Hozircha bizda bu tugma bosilganda o'zgartiradigan biror narsa yo'q, shuning uchun keling, view'imizga fon (background) qo'shaylik. Tugmamizni ZStack ichiga joylaymiz, va tugmadan oldin orqa fon qatlamini qo'shamiz: Color.yellow, va uni .edgesIgnoringSafeArea(.all) qilamiz. Endi shu fonning rangini o'zgartirish uchun, @State var backgroundColor nomli, Color turidagi o'zgaruvchi yaratamiz, va uni .yellowga tenglashtiramiz — so'ngra shu backgroundColorni orqa fon sifatida ishlatamiz. Endi "Delete" tugmasini bosganimizda, shunchaki shu backgroundColorni .redga o'zgartiramiz — bu, albatta, shunchaki shu tugmani bosganimizda biror kod ishga tushishini isbotlash uchun, xolos:

@State var showAlert: Bool = false
@State var backgroundColor: Color = .yellow

var body: some View {
    ZStack {
        backgroundColor
            .edgesIgnoringSafeArea(.all)

        Button("Click here") {
            showAlert.toggle()
        }
        .alert(isPresented: $showAlert) {
            Alert(
                title: Text("This is the title"),
                message: Text("We will describe the error"),
                primaryButton: .destructive(Text("Delete"), action: {
                    backgroundColor = .red
                }),
                secondaryButton: .cancel()
            )
        }
    }
}

Canvas'ni qaytadan ishga tushiramiz: tugmamizni bossak, sarlavha, xabar, va sukut bo'yicha hech narsa qilmaydigan, shunchaki alert'ni yopib qo'yadigan Cancel tugmamizni ko'ramiz, albatta Delete tugmamiz ham bor. Va Delete tugmasini bosganimizda, endi bizda custom harakat ishlaydi — bu shunchaki fon rangini qizilga o'zgartiradi. Delete'ni bossangiz, bu haqiqatan ham ishlayotganini ko'rasiz. Mana shu — alert ko'rsatishning asosiy mantig'i.


Alert funksiyasini body'dan tashqariga chiqarish

Agar siz ushbu kursni kuzatib borgan bo'lsangiz, meni bilsangiz — siz bilasizki, men body'ni iloji boricha toza va tartibli saqlashni juda yoqtiraman. Va bu, menimcha, hozir biroz tartibsizroq ko'rinishni boshlamoqda. Shuning uchun shu alert funksiyasini olib, bodydan ajratib chiqaramiz.

bodydan tashqarida, pastda, getAlert nomli funksiya yaratamiz, qavslarini ochib-yopamiz, va bu funksiya Alert qaytarishi kerak — shuning uchun o'q (->) qo'shamiz, bu, asosan, "qaytaradi" degani. So'ngra Alert deb yozib, jingalak qavslarni ochamiz — demak, bu funksiya bir Alert qaytaradi. Ichiga return yozib, avvalgi Alertimizni qirqib olib, shu yerga joylaymiz, va bodydagi tarkib o'rniga shunchaki getAlert()ni chaqiramiz:

func getAlert() -> Alert {
    return Alert(
        title: Text("This is the title"),
        message: Text("We will describe the error"),
        primaryButton: .destructive(Text("Delete"), action: {
            backgroundColor = .red
        }),
        secondaryButton: .cancel()
    )
}

var body: some View {
    ZStack {
        backgroundColor
            .edgesIgnoringSafeArea(.all)

        Button("Click here") {
            showAlert.toggle()
        }
        .alert(isPresented: $showAlert) {
            getAlert()
        }
    }
}

Hammasi muvaffaqiyatli yig'iladi, va yana ko'rib turganingizdek, body endi qanchalik toza va tartibli ko'rinmoqda, ammo biz hamon o'zimizning alert'imizni olamiz. "Resume" tugmasini bosib, tugma hamon ishlayotganiga ishonch hosil qilamiz — albatta, ishlayveradi.


Alert'ni dinamik qilish: title va message o'zgaruvchilari

Videoni tugatishdan oldin, sizlarga bu alert'larni moslashtirishning bir nechta usulini ko'rsatib o'tmoqchiman, chunki ba'zan siz shu matnni biroz dinamik qilishni xohlaysiz — har doim aynan bir xil sarlavha yoki aynan bir xil tasvirni ko'rsatishni xohlamaysiz, yoki bir xil view'da turlicha alert'larga ega bo'lishni xohlaysiz. Keling, buni moslashtirishni boshlaylik — men sizlarni buni qilishning bir nechta turli usuli orqali olib o'taman.

Birinchi usul — sizda sarlavha va xabar bo'lgan, ammo faqat standart tugmalar bo'lgan vaziyatlar uchun, ya'ni bizda primary va secondary tugma yo'q vaziyat uchun. Buning uchun ekranning yuqorisida yana ikkita o'zgaruvchi yarataman: @State var alertTitle, turi String, va hozircha buni bo'sh string'ga tenglashtiraman; xuddi shunday @State var alertMessage, turi String, va buni ham hozircha bo'sh xabarga tenglashtiraman.

Endi getAlert funksiyamizda, avvalgi butun bo'limni izohga olib (bosish: Command + /), o'rniga title, message, dismissButton variantidan foydalanadigan alert qaytaramiz. Sarlavha — bu Text(alertTitle), xabar esa xuddi shunday — Text(alertMessage), dismiss (yopish) tugmasi uchun esa labelga ega .default variantidan foydalanamiz, va matn sifatida shunchaki "OK" deb yozamiz:

@State var alertTitle: String = ""
@State var alertMessage: String = ""

func getAlert() -> Alert {
    return Alert(
        title: Text(alertTitle),
        message: Text(alertMessage),
        dismissButton: .default(Text("OK"))
    )
}

Endi har safar getAlertni chaqirganimizda, u shu paytdagi qanday bo'lsa, aynan shu sarlavha va xabar bilan alert ko'rsatadi — demak, biz alert'ni ko'rsatishdan oldin, shunchaki sarlavha va xabarni o'zgartirib qo'yishimiz mumkin.

Buni ko'rsatish uchun, tugmamizni VStack ichiga joylayman (alert'ni esa qirqib, VStackning pastiga olib o'taman), va shu tugmani nusxalab, yana bir tugma qo'shaman — endi ekranimizda ikkita tugma bor. Ularning sarlavhalarini "Button 1" va "Button 2" deb o'zgartiramiz. Endi har safar alert'ni ko'rsatishdan oldin, sarlavha va xabarni o'zgartiramiz:

VStack {
    Button("Button 1") {
        alertTitle = "Error uploading video"
        alertMessage = "The video could not be uploaded"
        showAlert.toggle()
    }

    Button("Button 2") {
        alertTitle = "✅ Successfully uploaded video"
        alertMessage = "Your video is now public"
        showAlert.toggle()
    }
}
.alert(isPresented: $showAlert) {
    getAlert()
}

Aytmoqchi, bunday alert'larga men emoji qo'shishni juda yoqtiraman — buning uchun Control + Command + Bo'shliq tugmalarini bosib, Xcode'dagi standart emoji'larni chiqarib olishingiz mumkin. Endi canvas'da "Resume" tugmasini bosib, "Button 1"ni bossam — "Error uploading video" sarlavhasi va "The video could not be uploaded" xabarini ko'raman. "Button 2"ni bossam esa — "Successfully uploaded video" va "Your video is now public" xabarini ko'raman.

Demak, biz shunchaki shu oddiy usuldan foydalanib, alert'ni dinamik ravishda o'zgartirishimiz mumkin, va pastda hamon o'sha bir xil, statik getAlert funksiyamiz bor. Ammo shu narsani alohida ta'kidlab o'tmoqchiman: agar shu usuldan foydalansangiz, sarlavha va xabarni har safar yangilab borishingizga ishonch hosil qiling — chunki, masalan, "Button 1"ni bosib sarlavha va xabarni yangilasak, keyin "Button 2" uchun xabarni yangilashni unutsak, bizda hamon birinchi tugmadan qolgan eski xabar turib qolaveradi. Shuning uchun, agar shu usulni ishlatsangiz, har doim ikkala o'zgaruvchini ham yangilab borishni unutmang.


Eng qulay usul: enum yordamida alert'larni boshqarish

Men bu video uzun bo'lmaydi degandim, ammo sizlarga alert'larimizni moslashtirishning yana bir, juda muhim usulini ko'rsatmoqchiman — bu esa enum yordamida amalga oshiriladi, va bu, ehtimol, haqiqiy ilovalarda buni qilishning eng keng tarqalgan usuli.

Bu usul uchun maxsus enum yaratamiz — buni oldingi videoda ham qilgan edik. enum MyAlerts deb yozib, qavslarni ochamiz, va shu enum ichiga barcha mumkin bo'lgan alert turlarini qo'shishimiz kerak: birinchisi case success, ikkinchisi esa case error. Agar ekraningizda turli xil xatoliklar bo'lsa, bu yerga yana ko'proq case qo'shishingiz mumkin, ammo hozircha buni faqat success va error bilan qoldiramiz:

enum MyAlerts {
    case success
    case error
}

Endi sarlavha va xabar o'zgaruvchilariga ega bo'lish o'rniga, biz shunchaki qaysi alert'ni ko'rsatmoqchi ekanligimizni boshqaradigan bitta state o'zgaruvchiga ega bo'lamiz: @State var alertType, turi — MyAlerts. Buni optional qilish uchun savol belgisi qo'shamiz, va boshida nilga tenglashtiramiz — demak, bu o'zgaruvchi har doim nil yoki shu ikki holatdan (success/error) biri bo'ladi. Endi bizga sarlavha va xabar o'zgaruvchilari shart emas.

Kodimizda esa, sarlavha va xabarni o'zgartirish o'rniga, shunchaki shu alertTypeni o'zgartiramiz: "Button 1" uchun buni .errorga tenglashtiramiz, "Button 2" uchun esa .successga tenglashtiramiz.

Endi getAlert funksiyamizda, avvalgisini chaqirish o'rniga, switch bayonotidan foydalanamiz: switch alertType deb yozamiz — bu yuqoridagi o'zgaruvchimiz, va u har doim success, error yoki nil bo'lishi mumkin, shuning uchun switch'da shu barcha holatlarni ko'rib chiqishimiz kerak.

Avval case .error: agar xatolik bo'lsa, shu yerda alert'ni moslashtirib, nima xohlasak, shuni qaytarishimiz mumkin — keling, shunchaki title variantidan foydalanib, Text("There was an error") deb yozaylik. Keyingi holat — case .success: agar muvaffaqiyatli bo'lsa, title, message, dismissButton variantidan foydalanamiz — sarlavha Text("This was a success") bo'ladi, xabar esa kerak emas, shuning uchun nil qoldiramiz, dismiss tugmasi uchun esa actionga ega .default variantidan foydalanamiz (chunki bizda allaqachon shu tayyor turib bor) — matn sifatida Text("OK") yozamiz, harakat uchun esa Enter bosib, shunchaki fon rangini .greenga o'zgartiramiz.

Va nihoyat, bizga default holat ham kerak — bu, biz alert turini sozlashni unutib qo'yganimizda yuz beradigan vaziyat uchun. Boshida alertTypeni nil qilib qo'yganmiz, va alert'ni ko'rsatishdan oldin uni .error yoki .successga o'rnatamiz — ammo agar buni unutib qo'ysak, shu holatni ham boshqarishimiz kerak. Shu yerda shunchaki Text("Error")ga ega bo'lgan alert qaytaramiz — bu, albatta, hech qachon yuz bermasligi kerak, chunki biz alert turini har doim oldindan o'rnatamiz:

@State var alertType: MyAlerts? = nil

func getAlert() -> Alert {
    switch alertType {
    case .error:
        return Alert(title: Text("There was an error"))
    case .success:
        return Alert(
            title: Text("This was a success"),
            message: nil,
            dismissButton: .default(Text("OK"), action: {
                backgroundColor = .green
            })
        )
    default:
        return Alert(title: Text("Error"))
    }
}

var body: some View {
    ZStack {
        backgroundColor
            .edgesIgnoringSafeArea(.all)

        VStack {
            Button("Button 1") {
                alertType = .error
                showAlert.toggle()
            }

            Button("Button 2") {
                alertType = .success
                showAlert.toggle()
            }
        }
        .alert(isPresented: $showAlert) {
            getAlert()
        }
    }
}

Keling, buni sinab ko'raylik: "Button 1"ni bosganimizda, alertType .errorga o'rnatiladi, so'ngra showAlertni chaqiramiz. showAlertni almashtirganimizda, bu getAlert funksiyasini chaqiradi, getAlert esa alertType ustida switch qiladi — shu paytdagi qiymat qanday bo'lsa, aynan shunga mos alert'ni ko'rsatadi. Demak, "Button 1" — "There was an error", "OK". "Button 2" — "This was a success", "OK", va "Button 2"ni bosganimizda fon rangi yashilga aylanadi — bu, chunki bizda muvaffaqiyat va xatolik uchun butunlay alohida alert'lar bor.


Xulosa

Mana shu — ushbu video uchun shu, xolos. Sizlarga alert qanday ko'rsatilishini, va uni qanday dinamik qilib, ekraningizdagi alert'larni haqiqatan ham moslashtirishni ko'rsatmoqchi edim. Bu enum usuli, ehtimol, eng keng tarqalgani, chunki u orqali qaysi alert'ni ko'rsatmoqchi ekanligingizni haqiqatan ham moslashtirishingiz mumkin. Ammo endi siz alert sarlavhasi va xabarini o'zgaruvchilar orqali boshqarishning boshqa usulini ham bilasiz, yoki alert'ni to'g'ridan-to'g'ri qo'shishingiz ham mumkin, albatta. O'z maxsus pop-up oynalaringizni yaratish usullari ham bor — agar standart formatlash sizga yoqmasa, buni ham qilishingiz mumkin.

Ammo boshlang'ichlar uchun, hamda MVP (minimal ishlaydigan mahsulot) yaratayotganlar yoki endigina boshlaganlar uchun aytardim: alert'lar juda keng tarqalgan, chunki ular juda sodda va tez qo'shiladi — bu esa, ayniqsa xatoliklarni boshqarish yoki foydalanuvchiga ko'rsatmoqchi bo'lgan muvaffaqiyat xabarlari uchun juda foydali.

Demak, endi siz alert mutaxassisisiz! Umid qilamanki, ushbu video sizlarga yoqdi. Keyingi videoda esa action sheetlarni ko'rib chiqamiz — action sheet'lar xuddi alert'larga o'xshaydi, faqat ular ekranning pastida paydo bo'ladi, va siz ularga ancha ko'proq tugma qo'shishingiz mumkin. Shuning uchun, agar sizda shu ikki tugmali alert yetarli bo'lmaydigan vaziyat bo'lsa, biz action sheet'dan foydalanib, to'rt, besh, hatto o'n tugmani ko'rsatishimiz mumkin bo'ladi.

Umid qilamanki, sizlar ham shu mavzuga qiziqib qoldingiz. Har doimgidek, men — Nik, bu Swiftful Thinking, keyingi videoda ko'rishamiz!

Buy mea coffee