Published on

SwiftUI-da Init va Enum-lardan qanday foydalanish kerak

Authors

Hammaga salom! Men — Nik, va ushbu videoda biz initlar va enumlar haqida gaplashamiz. Bu, ehtimol, ushbu kursdagi eng qiyin videolardan biri bo'ladi — ammo bu mavzuning o'zi qiyin bo'lgani uchun emas, balki ko'p narsa "sahna ortida" sodir bo'lgani va unchalik ko'zga ko'rinmasligi sababli shunday.

Avval initlar haqida gaplashamiz. "Init" so'zi initializerdan qisqartirilgan, va bu, asosan, sizning view'ingizni sozlash uchun ishga tushadigan funksiya, xolos. Biz shu init funksiyalarini o'zimizning maxsus o'zgaruvchilarimiz bilan moslashtirishimiz mumkin. Masalan, agar ilovamizda bir xil view'dan bir necha marta foydalanmoqchi bo'lsak, va har safar fon rangini o'zgartirishni xohlasak — biz shu init'ga maxsus rang qo'shib qo'yishimiz mumkin. Shunda har safar shu view'ni chaqirganimizda, shunchaki rangni o'zgartirib, bir xil view'ni boshqa rangda olishimiz mumkin bo'ladi: bir safar qizil, bir safar ko'k — va rangni o'zgartirish bilan bog'liq barcha mantiqni init o'zi bajaradi.

O'ylaymanki, bu eshitilganidan ancha osonroq, shuning uchun buni sizlar uchun soddalashtirib tushuntirishga harakat qilaman. SwiftUI-da har doim o'z maxsus initializer'imizdan foydalanishimiz shart emas, ammo ba'zan — ayniqsa murakkabroq va ilg'or ilovalar yarata boshlaganimizda — bu kerak bo'lib qoladi. Va init'ingizni qanday moslashtirishni bilish, uzoq muddatda sizga juda foydali bo'ladi.

Shundan keyin biz qisqacha enumlar (enumeration — sanab o'tilgan turlar) haqida gaplashamiz. Bu, kodimizda ma'lum narsalarni turkumlashning oson usuli. Masalan, agar bizda xarita ilovasi bo'lsa va doim shimol, janub, sharq yoki g'arb kabi yo'nalishlarga murojaat qilishimiz kerak bo'lsa — boshlang'ich dasturchi bu uchun shunchaki "shimol", "janub", "sharq", "g'arb" deb string yozishni boshlaydi. Ammo tajribali dasturchi esa enum yaratib, unda shimol, janub, sharq va g'arb uchun to'rt case belgilaydi. Shunda kodingizda har safar "shimol" deb yozish o'rniga, siz shunchaki shu enum'ga murojaat qilasiz. Bu, oxir-oqibatda, ilovangizga foyda keltiradi — chunki bu xatoliklarning (typo) oldini oladi, hamda ilovangizni ancha tartibli va samarali qiladi.


Yangi loyiha fayli yaratish

Keling, Xcode loyihasiga qaytaylik va shu video uchun yangi fayl yarataylik. Navigator'da o'ng tugmani bosib, yangi fayl yaratamiz — bu SwiftUI View bo'ladi, va biz init'lar va enum'lar haqida gaplashayotganimiz uchun, buni InitializerBootcamp deb ataymiz. "Create" tugmasini bosamiz, va fayl yaratilgach, canvas'da ulanganimizga ishonch hosil qilish uchun "Resume" tugmasini bosamiz — va endi kod yozishni boshlaymiz.


Boshlang'ich view yaratish

Avval kichik bir view yarataylik. VStack qo'shamiz, ichiga Text yordamida yuqorida bir raqam — "5" — chiqaramiz, ostida esa yana bir Text bilan sarlavha qo'shamiz — masalan, "apples" (bu sondagi olmalar nechta ekanligini bildiradi). Bu view'ga kengligi 150, balandligi 150 bo'lgan frame beramiz, tekislashni (alignment) esa o'chirib qo'yamiz. Endi bu view'ga Color.red fonini beramiz — chunki bular olmalar — va ko'rinishi yaxshiroq bo'lishi uchun burchak radiusini (corner radius), aytaylik, 10 qilib qo'yamiz.

Endi shriftlarni tezda sozlaylik: "apples" matnini .headline shriftida, oq rangda (foregroundColor(.white)) qilamiz. "5" raqamini esa biroz kattaroq, .largeTitle shriftida, yana oq rangda, va tag chizig'i (underline) bilan beramiz. Va VStack elementlari orasiga, aytaylik, 12 qiymatida bo'shliq (spacing) qo'shamiz:

struct InitializerBootcamp: View {

    var body: some View {
        VStack(spacing: 12) {
            Text("5")
                .font(.largeTitle)
                .foregroundColor(.white)
                .underline()

            Text("apples")
                .font(.headline)
                .foregroundColor(.white)
        }
        .frame(width: 150, height: 150)
        .background(Color.red)
        .cornerRadius(10)
    }
}

Bu ko'rinish ancha yaxshi chiqdi. Endi esa init'lar mavzusiga o'tamiz.


O'zgaruvchilarni bodydan tashqariga chiqarish

Hozirgacha ushbu kursda biror o'zgaruvchi qo'shganimizda — bu 150 kabi raqam bo'lsin, qizil rang bo'lsin, yoki "apples" matni bo'lsin — biz buni to'g'ridan-to'g'ri kodimiz ichiga yozganmiz, xuddi hozir qilganimizdek. Ammo biz bu ma'lumotni saqlaydigan o'zgaruvchilarni bodydan tashqarida ham yaratishimiz mumkin. Keling, buni tezda qilib ko'raylik — avval fon rangidan boshlaymiz.

bodydan oldin, struct ichida, backgroundColor nomli, Color turidagi konstanta yaratamiz va uni .redga tenglashtiramiz. Buni Color turida e'lon qilamiz, chunki tizimga bu qanday o'zgaruvchi ekanligini — raqam, rang yoki boshqa bir narsa ekanligini — bildirishimiz kerak. Endi kodimizda Color.redni to'g'ridan-to'g'ri yozish o'rniga, shunchaki shu backgroundColor o'zgaruvchisiga murojaat qilamiz:

struct InitializerBootcamp: View {

    let backgroundColor: Color = .red

    var body: some View {
        VStack(spacing: 12) {
            Text("5")
                .font(.largeTitle)
                .foregroundColor(.white)
                .underline()

            Text("apples")
                .font(.headline)
                .foregroundColor(.white)
        }
        .frame(width: 150, height: 150)
        .background(backgroundColor)
        .cornerRadius(10)
    }
}

Canvas'da "Resume" tugmasini bosganimda, hamon qizil rangda ekanligini ko'ramiz. Va agar shu backgroundColorni, aytaylik, ko'k ranggа o'zgartirsam, canvas mos ravishda yangilanadi. Bu juda foydali: agar bir xil o'zgaruvchidan view ichida bir necha marta foydalanmoqchi bo'lsangiz — masalan, fon ham qizil bo'lsa, va boshqa bir narsa ham qizil fonga muhtoj bo'lsa — bu o'zgaruvchini yuqoriga chiqarib qo'yish ancha qulay, chunki kodingiz bo'ylab izlab yurish o'rniga, uni faqat shu bir joydan osongina o'zgartirish mumkin bo'ladi. Hozircha esa buni yana qizilga qaytarib qo'yamiz.


Standart qiymatni olib tashlash — init talabga aylanadi

Endi buni bir necha qadam oldinga olib boraylik. Hozircha biz bu view'ni qizil fon bilan ishga tushiramiz, ammo agar shu view'dan ilovamizda bir necha marta, har safar boshqa rang bilan foydalanmoqchi bo'lsak-chi? Bir safar qizil, boshqa safar to'q sariq yoki ko'k kerak bo'lsa-chi?

Buning uchun shunchaki backgroundColorga standart qiymat bermaymiz:

let backgroundColor: Color

Endi shu view'ni — ya'ni InitializerBootcampni — ishga tushirmoqchi bo'lganimizda, u bizdan fon rangini so'raydi. Preview'da xatolik chiqadi, chunki InitializerBootcampni ishga tushirayotganda, u bizdan qaysi fon rangini xohlaymiz, deb so'rayapti. Agar shu xatolik ustidagi "Fix" tugmasini bossam, InitializerBootcampimizda endi backgroundColor parametri borligini, va u bizdan rang so'rayotganini ko'ramiz. Agar shu yerga, masalan, .purple deb yozsam va canvas'ni qaytadan ishga tushirsam, rang o'zgarganini ko'raman.

Demak, ilovamiz bo'ylab har safar shu view'ni — bu ekran, tugma, menyu yoki shunga o'xshash narsa bo'lishi mumkin — ishga tushirganimizda, rangni o'zgartirish uchun shu bitta kichik o'zgaruvchini o'zgartirsak bo'ladi, qolgan kod esa o'zgarishsiz qoladi. Hozir biz .purple ishlatamiz, ammo buni xohlagan rangga — masalan, .orangega — o'zgartirishimiz mumkin, va preview mos ravishda yangilanadi. Bu juda qulay.


count va title uchun ham xuddi shunday

Biz bu o'zgaruvchilarni body ichidagi istalgan element uchun, view'dan tashqarida yaratishimiz mumkin — masalan, raqam, matn yoki kenglik uchun ham. Keling, shu narsalarni ham view'dan ajratib chiqaraylik.

Avval shu raqam uchun: count nomli, Int (butun son) turidagi konstanta yaratamiz va uni 5ga tenglashtiramiz. Swift-da bir o'zgaruvchini string ichiga joylashtirish uchun, teskari chiziqcha (\) qo'yib, qavs ochib-yopib, ichiga shu o'zgaruvchiga murojaat qilishimiz kifoya — bu yerda count:

let count: Int = 5
Text("\(count)")

Canvas'ni qaytadan ishga tushirsam, hamon bog'lanib turishi kerak, va agar shu countni 25ga o'zgartirib, canvas'ni qaytadan ishlatsam, raqamimiz 25ga o'zgaradi. Va yana, agar bunga boshlang'ich qiymat bermasam, har safar shu InitializerBootcampni ishga tushirganimizda, u bizdan boshlang'ich qiymat so'raydi — shunda men, masalan, 50 yoki 55 qiymatlarini berishim mumkin, va canvas 55ga yangilanadi.

Demak, endi initializer bizdan rang va son so'raydi. Keling, sarlavha (title) uchun ham xuddi shunday qilaylik: title nomli, String turidagi konstanta yaratamiz va uni "apples"ga tenglashtiramiz. Endi bu butun matn o'rniga, shunchaki titleni qo'yamiz:

Text(title)

"Resume" tugmasini bossam, hammasi muvaffaqiyatli yig'iladi, va agar shu "apples"ni "oranges"ga o'zgartirib, qaytadan ishlatsam, endi 55 oranges ko'rinadi. Va yana bir bor — agar standart qiymatni olib tashlasak, har safar shu view'ni ishga tushirganimizda, u bizdan sarlavha so'raydi. Bu yerga, masalan, "peaches" deb yozib, canvas'ni ishlatsam, endi 55 peaches ko'rinadi — chunki shu initializer'ga to'q sariq rang, 55 va "peaches" qiymatlarini bergan bo'lamiz.


SwiftUI view uchun avtomatik yaratiladigan init

Keling, bu yerda aslida nima sodir bo'layotganini chuqurroq ko'rib chiqaylik. SwiftUI bizning view'imiz uchun avtomatik ravishda bir initializer yaratadi — aynan shu sababdan u bizdan fon rangi va sonni so'raydi. Ammo aslida nima sodir bo'lmoqda — u init deb ataladigan funksiya yaratadi. Agar init deb yozib, Enter bossak, u bizdan parametrlar va amallarni so'raydi. Init — bu, asosan, shu view yaratilishi bilanoq ishga tushadigan, view'ni yangilash va yaratish uchun mo'ljallangan boshlang'ich funksiya.

Demak, hozirgi holatda bizning init'imiz quyidagicha ko'rinadi: backgroundColor (Color turida), count (Int turida) va title (String turida) — aynan shu uchta narsa, xolos. Va u self.backgroundColorni, ya'ni shu view ichidagi o'zgaruvchini, funksiyaga uzatilgan backgroundColorga tenglashtiradi, xuddi shunday self.countni uzatilgan countga, self.titleni esa uzatilgan titlega tenglashtiradi:

init(backgroundColor: Color, count: Int, title: String) {
    self.backgroundColor = backgroundColor
    self.count = count
    self.title = title
}

Demak, view ichidagi count — bu bitta narsa, funksiyaga uzatilayotgan count esa boshqa narsa, va bu funksiya — aynan biz pastda InitializerBootcampni chaqirganimizda ko'rib turganimiz narsa. Bu — bizning haqiqiy init bayonotimiz, va standart holatda init bayonoti aynan shunday ko'rinadi — ammo SwiftUI juda aqlli, va buni biz uchun avtomatik yaratib beradi, shuning uchun bizga bu kodni qo'lda yozish shart emas edi. Ammo bu, biz avval yozgan kodimiz bilan aynan bir xil ishni bajaradi, faqat bu safar buni qo'lda yozmadik.

Va men sizlarga buni ko'rsatishimning sababi shu: biz o'z init'imizni ham moslashtirishimiz mumkin. Bu nimani anglatadi?


Init-ni moslashtirish

Hozircha bizning init'imiz fon rangi, son va sarlavha so'rayapti. Endi tasavvur qiling: biz shu view'dan olma va apelsinlar uchun qayta-qayta foydalanmoqchimiz, va har safar olma bo'lganda qizil rangdan, apelsin bo'lganda esa to'q sariq rangdan foydalanmoqchimiz.

Buni hozirgi uzun yo'l bilan ham qilishimiz mumkin: olmalar uchun fon rangini qizilga, sarlavhani esa "apples"ga o'zgartiramiz — canvas'ni ishlatsak, qizil olmalarimiz paydo bo'ladi. Apelsinlarga qaytmoqchi bo'lsak, rangni to'q sariqqa, sarlavhani esa "oranges"ga o'zgartiramiz. Ammo shu yerda payqaymizki, fon rangi va sarlavha bir-biriga bog'liq — har safar sarlavha "oranges" bo'lganda, fon rangi ham to'q sariq bo'lishi kerak. Demak, bir xil ma'lumotni ikki marta qo'lda kiritish kodimizda muammoga olib kelishi mumkin.

Buning oldini olish uchun, biz o'zimizning maxsus init'imizni yaratishimiz mumkin. Bu yerda fon rangini parametr sifatida qabul qilish o'rniga, buni init'dan olib tashlaymiz — endi init faqat son va sarlavhani so'raydi. Va biz shunchaki: agar sarlavha "apples"ga teng bo'lsa, self.backgroundColorni .redga tenglashtiramiz; aks holda (ya'ni sarlavha "apples" bo'lmasa), self.backgroundColorni .orangega tenglashtiramiz, deb yozamiz. Endi fon rangi parametrini view'dan ham olib tashlashimiz mumkin:

struct InitializerBootcamp: View {

    let backgroundColor: Color
    let count: Int
    let title: String

    init(count: Int, title: String) {
        self.count = count
        self.title = title

        if title == "apples" {
            self.backgroundColor = .red
        } else {
            self.backgroundColor = .orange
        }
    }

    var body: some View {
        VStack(spacing: 12) {
            Text("\(count)")
                .font(.largeTitle)
                .foregroundColor(.white)
                .underline()

            Text(title)
                .font(.headline)
                .foregroundColor(.white)
        }
        .frame(width: 150, height: 150)
        .background(backgroundColor)
        .cornerRadius(10)
    }
}

Endi shu view'ni ishga tushirganimizda, u bizdan faqat son va sarlavhani so'raydi, va agar sarlavha "apples" bo'lsa, rang avtomatik ravishda qizil bo'ladi, aks holda esa to'q sariq bo'ladi. Buni sinab ko'raylik: countga 5, sarlavhaga esa "apples"ni qo'yamiz:

InitializerBootcamp(count: 5, title: "apples")

Canvas'ni ishlatsak, fon avtomatik ravishda qizil bo'lib chiqadi — biz init'ga "qizil qil" deb aytmaganmiz ham, u sarlavha "apples" ekanligini bilib, fon rangini o'zi qizil qilib qo'ydi. Va agar sarlavhani "oranges"ga o'zgartirsam, fon rangi darrov to'q sariqqa o'tadi — bu juda ajoyib.

Kelajakda if/else va shartli bayonotlar haqida alohida, batafsil video qilaman — agar shu qism sizga biroz chalkash tuyulgan bo'lsa, xavotir olmang, buni keyinroq batafsil ko'rib chiqamiz, chunki bular ilovalarimizda mantiq yozishni boshlaganimizda juda-juda muhim va kuchli vositalar hisoblanadi.


Enum kiritish: Fruit

Ammo videoni tugatishdan oldin, sizlarni enumlar bilan tanishtirmoqchiman. Swift-da, agar bizda bir nechta variant orasidan tanlash kerak bo'lgan vaziyat bo'lsa — masalan, bizda mevalar bor va biz olma bilan apelsin orasida almashtirishni xohlasak — "apples" deb to'g'ridan-to'g'ri matn yozish o'rniga, biz maxsus enumdan foydalanishimiz mumkin.

Keling, buni tezda yarataylik: shu init'dan tashqarida enum qo'shamiz, uni Fruit deb ataymiz, va endi unga barcha mumkin bo'lgan meva variantlarini aytishimiz kerak. Hozirgi ilovamiz uchun shunchaki case apple va case orange deb yozamiz:

enum Fruit {
    case apple
    case orange
}

Endi, matn (string) o'rniga shu Fruit turidagi enum'dan foydalansak, unga "olmani ishlat" yoki "apelsinni ishlat" deyishimiz mumkin bo'ladi. Sarlavha so'rash va uni to'g'ridan-to'g'ri matn sifatida kiritish o'rniga, endi biz meva (fruit) so'raymiz, va bu Fruit turida bo'ladi. Eski if bayonotini va sarlavha parametrini o'chirib tashlaymiz, va endi shunday yozamiz: agar fruit — bu yerda parametr nomi ham kichik harfli fruit bo'lsin — nuqta bilan boshlangan qiymatga teng bo'lsa... Endi shu yerda fruit o'zgaruvchimiz uchun faqat ikki variant borligini ko'ramiz: u doim olma yoki apelsin bo'ladi.

Agar u olma bo'lsa: self.titleni "apples"ga, self.backgroundColorni esa .redga tenglashtiramiz. Aks holda, ya'ni olma bo'lmasa, demak u apelsin bo'lishi kerak: self.titleni "oranges"ga, self.backgroundColorni esa .orangega tenglashtiramiz:

struct InitializerBootcamp: View {

    let backgroundColor: Color
    let count: Int
    let title: String

    init(count: Int, fruit: Fruit) {
        self.count = count

        if fruit == .apple {
            self.title = "apples"
            self.backgroundColor = .red
        } else {
            self.title = "oranges"
            self.backgroundColor = .orange
        }
    }

    var body: some View {
        VStack(spacing: 12) {
            Text("\(count)")
                .font(.largeTitle)
                .foregroundColor(.white)
                .underline()

            Text(title)
                .font(.headline)
                .foregroundColor(.white)
        }
        .frame(width: 150, height: 150)
        .background(backgroundColor)
        .cornerRadius(10)
    }
}

Endi shu view'ni ishga tushirganimizda, initializer bizdan faqat son va mevani so'raydi. countga 100 qo'yamiz, va fruit uchun nuqta qo'yganimizda, faqat ikki variant ko'ramiz — .apple yoki .orange. Endi "apple" so'zini qo'lda yozishimiz, "orange" so'zini yozishimiz, qizil rangdan yoki to'q sariq rangdan foydalanishimiz shart emas — biz shunchaki .apple deb yozsak, qolgan hammasini shu initializer o'zi hal qiladi:

InitializerBootcamp(count: 100, fruit: .apple)

Canvas'da "Resume" tugmasini bossam, endi bizda 100 apples borligini ko'raman.


Bir xil view'dan ikki xil natija

Sizlarga tezroq yana bir narsa ko'rsatib o'taman: keling, preview'imizdagi shu view'ni HStack ichiga joylaylik. HStack yozib, jingalak qavslarni ochamiz, mavjud InitializerBootcampimizni shu yerga olib kelamiz, va uning o'ng tomoniga yana bir InitializerBootcamp qo'shamiz — bu safar countni 46, fruitni esa .orange qilib beramiz:

HStack {
    InitializerBootcamp(count: 100, fruit: .apple)
    InitializerBootcamp(count: 46, fruit: .orange)
}

Endi ekranimizda ko'ramiz: bir xil body kodidan foydalanib, biz ikki butunlay boshqacha view'ga ega bo'ldik — turli sonlar, turli ranglar, turli sarlavhalar bilan. Va biz shu view'larga shunchaki bir son va Fruit turidagi enum'dan iborat bitta qiymat berdik, xolos.


Xulosa

Kelajakdagi videoda if/else bayonotlariga yanada chuqurroq kirib boramiz, shuningdek enum'lardan hozirgidan ham samaraliroq qanday foydalanishni o'rganamiz. Ammo men ushbu videoda sizlarga shu mavzuga shunchaki bir kirish qilib bermoqchi edim — init'lar aslida qanday ishlashini tushuntirib berish uchun, chunki bundan keyingi ko'plab view'larimizda biz ma'lumotlarimizni bodydan tashqarida saqlaymiz, chunki bu ancha samaraliroq. Demak, ilovamizda to'g'ridan-to'g'ri "apple" deb yozish o'rniga, endi biz ma'lumotlarimizni bodydan tashqarida joylashgan o'zgaruvchilarga joylashtirib boramiz.

Umid qilamanki, ushbu video sizlarga yoqdi — bilaman, u biroz uzunroq va biroz qiyinroq, biroz chalkashroq edi, ammo biz aynan shu yerda foydali narsalarga kira boshlayapmiz, shuning uchun mavzuni kuzatib borayotganingizga, hammasini tushunib olayotganingizga ishonaman. Har doimgidek, men — Nik, bu Swiftful Thinking, keyingi videoda ko'rishamiz!

Buy mea coffee