- Published on
SwiftUI-da @FocusState-dan qanday foydalanish kerak
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Hammaga xush kelibsiz qaytib! Men — Nick, bu Swiftful Thinking — bu yerda biz Swift va SwiftUI-ga oid barcha narsalarni ko'rib chiqamiz, va umid qilaman, sizlar diqqatni jamlashga (focus) tayyorsiz, chunki bugun biz SwiftUI-dagi @FocusState property wrapper-ini ko'rib chiqamiz.
Bu eshitilganidan ancha murakkab tuyuladi, ammo aslida bu shunchaki bizga SwiftUI-da biror komponentni dasturiy ravishda tanlash yoki tanlovdan chiqarish imkonini beradi. Bu, asosan, TextField uchun ishlatiladi — biz ham aynan shuni misol qilib ko'rib chiqamiz. Avval, @FocusState paydo bo'lishidan oldin, TextField-ni dasturiy ravishda tanlab, qurilmada klaviaturani chiqarib yuborishning hech qanday yo'li yo'q edi. Ammo @FocusState bilan, biz aynan shuni — TextField-ni tanlab, klaviaturani dasturiy ravishda chiqarib yuborishni aniqlay olamiz. Shuningdek, kursorni bir TextField-dan ikkinchisiga, undan yana boshqasiga ko'chirishimiz ham mumkin.
Bu, ko'rinishidan, juda muhim narsa emasdek tuyulishi mumkin, ammo agar foydalanuvchilaringiz onboardingdan o'tayotgan bo'lsa, yoki o'z ismlarini va shunga o'xshash narsalarni yozayotgan bo'lsa, ushbu fokus holatlaridan foydalanish foydalanuvchi tajribasini sezilarli darajada yaxshilaydi. Shuning uchun bu, albatta, bilishingiz kerak bo'lgan narsa, va biz hozir bu haqida bilishingiz kerak bo'lgan hamma narsani ko'rib chiqamiz.
@FocusState— SwiftUI-da iOS 15dan boshlab paydo bo'lgan yangi xususiyat.
Yangi SwiftUI fayl yaratish
Ushbu video uchun yangi fayl yaratamiz — Navigator-da o'ng tugmani bosib, yangi fayl yaratamiz. Bu SwiftUI View bo'ladi, va unga FocusStateBootcamp deb nom beramiz. Create tugmasini bosamiz, ichiga kirgandan so'ng, canvas-da Resume tugmasini bosib, hammasi ulanganligiga ishonch hosil qilamiz.
Avval sodda bir misoldan boshlaymiz, so'ngra ilovalarimizda biroz ko'proq amaliy narsaga o'tamiz.
Boshlang'ich TextField
VStack yaratamiz, va unga oddiy bir TextField qo'shamiz:
VStack {
TextField("Add your name here...", text: $username)
}
Eslatma: Xcode-da TextField yozganingizda, iOS 15 uchun bir nechta yangi tugatish (completion) variant chiqadi, ammo men hozircha iOS 14 uslubidagi, sodda title va binding parametrlari bilan ishlayman.
Bunga binding string kerak, shuning uchun yuqorida state o'zgaruvchisi yaratamiz:
@State private var username: String = ""
Resume tugmasini bosib, ekranimizda shu TextField paydo bo'lganini ko'ramiz.
Ko'rinishini biroz yaxshilash
TextField-ga butunlikda 40 padding, balandligi 55 bo'lgan frame, eni .infinity bo'lgan frame, va kulrang fon (rangini biroz ochroq qilish uchun brightness(0.3) bilan) qo'shamiz. Burchak radiusini 10 qilamiz, va chap tomonga (leading) biroz padding qo'shib, uni ichkariga suramiz:
TextField("Add your name here...", text: $username)
.padding(.leading)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(
Color.gray
.brightness(0.3)
)
.cornerRadius(10)
.padding(40)
Simulatorga o'tish
Tezroq ko'rish uchun, ilovamizning App.swift fayliga o'tib, ushbu view-ni ilovaning birinchi view-i qilamiz, va simulatorda ishga tushiramiz.
Bizda shu TextField bor, va uni bossak, albatta, klaviatura chiqadi — bu odatdagi xulq-atvor. Ammo hozircha hech qanday FocusStatesiz, bu TextField-ni dasturiy ravishda bosib bo'lmaydi — foydalanuvchi to'g'ridan-to'g'ri shu yerga bosmasa, kursorni shu TextField ichiga kod orqali kiritishning hech qanday yo'li yo'q. Aynan shu joyda @FocusState o'z kuchini ko'rsatadi.
@FocusState qo'shish
Shu TextField-ga .focused modifikatorini qo'shamiz, va bu yerda shartni ko'rsatish kerak bo'ladi. Ko'rinib turibdiki, bu Boolean qiymatga ega FocusStatega bog'lanishi (binding) kerak.
Shuning uchun, yuqorida @FocusState qo'shamiz:
@FocusState private var usernameInFocus: Bool
Bu, albatta, Boolean bo'lishi kerak, va unga boshlang'ich qiymat berishimiz shart emas — u TextField asosida o'z qiymatini avtomatik qayd etadi.
Endi shu yerda $usernameInFocusni bog'laymiz:
TextField("Add your name here...", text: $username)
.focused($usernameInFocus)
.padding(.leading)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(
Color.gray
.brightness(0.3)
)
.cornerRadius(10)
.padding(40)
Demak, usernameInFocusning qiymati — shu TextField hozir bosilganmi yoki yo'qmi, ana shunga teng bo'ladi. Hozir ilovamda bu false bo'lar edi, va shu TextField-ni bosgan zahotim, usernameInFocusning qiymati truega aylanadi.
Va bu qanchalik oson bo'lsa, biz endi bu qiymatni dasturiy ravishda ham truega aylantirishimiz mumkin — agar buni true qilsak, biz pastda shu o'zgaruvchiga bog'langan bo'lganimiz uchun, TextField fokusga kiradi.
Tugma orqali fokusni almashtirish
Ekranimizga kichik bir tugma qo'shamiz:
Button("Toggle focus state") {
usernameInFocus.toggle()
}
Buni simulatorda ishga tushiramiz, va — bu @FocusStatening eng qiziqarli jihati: biz narsalarni fokusga dasturiy ravishda kiritishimiz va undan chiqarishimiz mumkin. Tugmani bossak, klaviatura chiqib keladi, TextField bosilgan holatga o'tadi — demak, biz endi qurilmadagi klaviaturani dasturiy ravishda ishga tushira olamiz, bu esa ilova yarata boshlaganingizda juda qulay bo'ladi.
Diqqat qiling: men TextField-ning o'ziga bosmayapman — men shu tugmani bosmoqdaman, va bu tugma ilovamning istalgan joyida bo'lishi mumkin.
Men ko'pchilik buni, masalan, "Submit" (yuborish) tugmasi sifatida ishlatganini ko'rganman: avval ism kiritilganligini tekshiradi, agar kiritilgan bo'lsa — yuboradi, agar kiritilmagan bo'lsa — xato xabarini ko'rsatish o'rniga, foydalanuvchini to'g'ridan-to'g'ri to'ldirilmagan TextFieldga yo'naltiradi.
Ekran ochilganda avtomatik fokusga olish
Yana bir qiziqarli narsa: aytaylik, foydalanuvchi shu ekranga kelganida, ularning ekranida faqat shu TextField bor — bu, masalan, onboarding bo'lishi mumkin, va ular shu yerga o'z ismlarini kiritadi. Biz ularning shu joyga bosishini kutib o'tirishni xohlamaymiz — aksincha, ular shu ekranga kelganlarining o'zidanoq, ularning darrov yozishga tayyor bo'lishini xohlaymiz.
Buning uchun .onAppear ishlatishimiz mumkin — ammo bu bevosita ishlamaydi, chunki view-ning to'liq render bo'lib bo'lishini kutishimiz kerak. Shuning uchun, kichik bir DispatchQueue.main.asyncAfter qo'shamiz — deadline: .now() + 0.5 (yarim soniya):
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.usernameInFocus = true
}
}
Ilovani yana ishga tushiramiz — endi ekran paydo bo'lganidan 0.5 soniya o'tib, biz fokus holatini avtomatik yoqamiz. Demak, foydalanuvchi shu ekranga kelishi bilanoq, biz allaqachon ularni TextField-ga bosilgan holatga keltirib, klaviaturani chiqarib qo'ygan bo'lamiz — ular hech narsa qilmasdan ham. Bu, onboarding jarayonini ancha tezlashtiradi.
Ikkinchi maydon: parol
Davom etaylik — biroz murakkabroq narsalarni ko'ramiz. Shu TextField-ni nusxalab, uning ostiga yana bittasini joylashtiramiz — bu safar "Add your password here...":
TextField("Add your password here...", text: $password)
.focused($passwordInFocus)
.padding(.leading)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(
Color.gray
.brightness(0.3)
)
.cornerRadius(10)
.padding(40)
Bunga mos yangi o'zgaruvchilar yaratamiz:
@State private var password: String = ""
@FocusState private var passwordInFocus: Bool
Simulatorda ishga tushirib ko'rsak — bizda ism va parol maydonlari bor.
"Sign up" tugmasi va validatsiya mantig'i
Endi yana bir tugma qo'shamiz — "Sign up 🚀" (emoji qo'shish uchun Control + Command + Spacebar orqali emoji panelidan foydalanishingiz mumkin):
Avval ilovamizda, agar foydalanuvchi ism va parolni kiritgan bo'lsa, ularni ro'yxatdan o'tkazamiz deb tasavvur qilaylik. Shuning uchun, usernameIsValid va passwordIsValid nomli konstantalar yaratamiz:
let usernameIsValid: Bool = !username.isEmpty
let passwordIsValid: Bool = !password.isEmpty
Albatta, haqiqiy ilovangizda ko'proq mantiq qo'shishingiz kerak bo'ladi — masalan, ism ma'lum miqdordagi belgidan iborat bo'lishi, so'kinish so'zlari bo'lmasligi, parolda kamida bitta bosh harf bo'lishi kabi talablar. Ammo ushbu misol uchun, biz shunchaki ularning bo'sh emasligini tekshiramiz.
Endi, agar ham usernameIsValid, ham passwordIsValid rost bo'lsa — biz shunchaki konsolga "Sign up" deb chop etamiz:
Button("Sign up 🚀") {
if usernameIsValid && passwordIsValid {
print("Sign up")
} else if usernameIsValid {
usernameInFocus = false
passwordInFocus = true
} else {
usernameInFocus = true
}
}
Mantiqni tushuntirib o'taylik:
- Agar ikkalasi ham to'g'ri (valid) bo'lsa — ro'yxatdan o'tish amalga oshiriladi.
- Agar faqat username to'g'ri bo'lsa — demak, ular hali parolni kiritmagan, shuning uchun biz
usernameInFocusnifalsega,passwordInFocusni esatruega o'rnatamiz, ya'ni parol maydonini fokusga olib boramiz. - Agar username ham noto'g'ri bo'lsa — demak, ular hali ismni ham kiritmagan, shuning uchun biz
usernameInFocusnitruega o'rnatamiz (passwordInFocusga endi ehtiyoj yo'q).
Tartibga solish
Tezroq formatlash uchun, VStack-ga spacing: 30 qo'shamiz, "Toggle focus state" tugmasini va .onAppear blokini hozircha yashirib qo'yamiz (izohga olamiz), chunki ular endi bizga kerak emas:
VStack(spacing: 30) {
TextField("Add your name here...", text: $username)
.focused($usernameInFocus)
// ...
TextField("Add your password here...", text: $password)
.focused($passwordInFocus)
// ...
Button("Sign up 🚀") {
if usernameIsValid && passwordIsValid {
print("Sign up")
} else if usernameIsValid {
usernameInFocus = false
passwordInFocus = true
} else {
usernameInFocus = true
}
}
}
Natijani sinab ko'rish
Ilovamizni qayta ishga tushiramiz. Bizda bo'sh ekran bor: ism, parol va "Sign up" tugmasi. Aytaylik, foydalanuvchi "Men ismimni ham, parolimni ham kiritmoqchi emasman" deb, to'g'ridan-to'g'ri "Sign up" tugmasini bosadi.
Biz qo'shgan mantiqqa ko'ra: agar ular tugmani bosganida username hali to'g'ri bo'lmasa, kod shu yerga tushadi, va biz usernameni fokusga olib boramiz. Demak, bu foydalanuvchi ro'yxatdan o'tishga harakat qildi, va darrov username klaviaturasiga yo'naltiriladi — ular o'zlarining ismni kiritishlari kerakligini tushunib qoladilar.
Shu yerga ismimni, masalan "nick123" deb kiritamiz, Return tugmasini bosamiz, va yana ro'yxatdan o'tishga harakat qilamiz. Bu safar usernamening to'g'ri ekanligini tekshirdi, shuning uchun usernameni fokusga olib borish o'rniga, biz parolni fokusga olib boramiz — endi u parolni kiritish kerakligini biladi.
Va agar "Sign up" tugmasini bossak-u, ikkalasi ham to'liq bo'lsa, biz konsolga shu chop etilgan natijalarni olamiz.
Bu — kichik bir ma'lumotni tekshirish (data validation) misoli. Bu, ayniqsa, agar sizda bir nechta maydon bo'lsa, va foydalanuvchiga shunchaki "Next" (keyingi) tugmasi orqali bir maydondan ikkinchisiga o'tish imkonini bermoqchi bo'lsangiz, juda qulay — kursor bir maydondan ikkinchisiga avtomatik ko'chiriladi, va bu istalgan forma yoki onboarding jarayonini ancha tezlashtiradi.
Ko'plab maydonlar uchun: enum orqali yagona FocusState
Videoni yakunlashdan oldin, sizlarga kichik bir mantiq ko'rsatib o'taman. Bizda ikkita fokus holati bor, va bu yaxshi, ammo agar ekranda besh ta TextField bo'lsa — masalan, ism, parol va boshqalar — har biri uchun alohida @FocusState yaratish unchalik mantiqiy emas.
Shuning uchun, buning o'rniga, biz enum yaratamiz:
enum OnboardingField: Hashable {
case username
case password
}
username va password uchun ikkita case yaratamiz. Endi, ikkita alohida @FocusState o'rniga, biz faqat bitta @FocusState ishlatamiz:
@FocusState private var fieldInFocus: OnboardingField?
Buni optional qilamiz, chunki standart holatda hech qaysi maydon fokusda bo'lmasligi kerak.
Hashable protokoliga moslashtirish
.focused modifikatori, ko'rib turganimizdek, Hashable protokoliga mos keladigan istalgan qiymatga bog'lanishi mumkin. Shuning uchun, bizning enum-imiz ham Hashable protokoliga mos kelishi kerak — yuqorida buni qo'shganimizni ko'rdingiz.
Endi $fieldInFocusga bog'laymiz, va har bir TextField uchun mos caseni belgilaymiz:
TextField("Add your name here...", text: $username)
.focused($fieldInFocus, equals: .username)
// ...
TextField("Add your password here...", text: $password)
.focused($fieldInFocus, equals: .password)
// ...
Mantiqni yangilash
Endi "Sign up" tugmasidagi mantiqni ham yangilaymiz — alohida Boollarni o'rnatish o'rniga, shunchaki fieldInFocusni mos casega tenglashtiramiz:
Button("Sign up 🚀") {
if usernameIsValid && passwordIsValid {
print("Sign up")
} else if usernameIsValid {
fieldInFocus = .password
} else {
fieldInFocus = .username
}
}
Ilovani qayta ishga tushiramiz — xuddi avvalgidek, tugmani bosib klaviaturani chiqarishimiz, ismimizni yozishimiz mumkin, va maydonlarni aynan oldingidek almashtirishimiz mumkin — faqat endi bizning kodimiz biroz ixchamroq, chunki biz enum yaratdik va barcha mumkin bo'lgan maydonlarni o'z ichiga olgan bitta fokus holatidan foydalandik — bu, albatta, .focused modifikatori bilan ishlatish uchun Hashable bo'lishi kerak.
Xavfsizlik: SecureFielddan foydalanish
Videoni yakunlashdan oldin, shuni alohida ta'kidlab o'tmoqchiman: parollar, ehtimol, himoyalangan bo'lishi kerak — biz parolning haqiqatan nima yozilayotganini ko'rib turishimiz kerak emas. Shuning uchun, bu holatda, oddiy TextField o'rniga, biz **SecureField**dan foydalanishimiz kerak:
SecureField("Add your password here...", text: $password)
.focused($fieldInFocus, equals: .password)
// ...
Ilovani qayta ishga tushirsak, parol maydoni endi himoyalangan ekanligini ko'ramiz: ismimni yozishim mumkin, ammo parolimni yozganimda, men nima yozayotganimni ko'ra olmayman.
Tezkor maslahat: foydalanuvchilar parol yoki shunga o'xshash maxfiy narsalarni kiritayotganda, albatta **
SecureField**dan foydalaning.
Xulosa
Endi siz TextField-larni qanday fokusga olib borishni bilasiz — bu juda qulay va juda kuchli xususiyat. Umid qilaman, bu video sizga yoqdi, va agar shu videodan biror narsa o'rgangan bo'lsangiz va u sizga foydali bo'lgan bo'lsa, obuna bo'lishni, layk bosishni va sharh qoldirishni unutmang.
Har doimgidek, men — Nick, bu Swiftful Thinking, va keyingi videoda ko'rishamiz!