- Published on
SwiftUI'da PreferenceKey bilan ishlash (Child'dan Parent'ga ma'lumot yuborish)
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Oddiy oqim va PreferenceKey nima?
SwiftUI da ma'lumotlar doim tepadan pastga (Parent dan Child ga) qarab harakatlanadi. Masalan, siz Parent oqimda o'zgaruvchi yaratasiz va uni ichkaridagi Child View'ga @Binding yordamida uzatasiz.
Lekin, qachondir .navigationTitle("Sarlavha") ni ishlatib ko'rganmisiz? Nega biz uni Child View ichida yozamiz, lekin u tashqaridagi Parent NavigationBar ga borib tushadi? Sababi, u yerda orqa fonda PreferenceKey ishlaydi! PreferenceKey ichki View lardan olingan ma'lumotlarni yig'ib, tepadagi Parent ga uzatish xususiyatiga ega.
1-Misol: Oddiy Yozuvni (String) tepaga uzatish
Keling, shaxsiy (Custom) sarlavha PreferenceKey yasaymiz. Buning uchun PreferenceKey protokoliga bo'ysunadigan Struct yaratamiz. Bizga ikkita narsa: defaultValue (boshlang'ich qiymat) va reduce (keyingi qiymat kelganda nima qilish kerakligi) funksiyasi kerak bo'ladi.
import SwiftUI
struct CustomTitlePreferenceKey: PreferenceKey {
static var defaultValue: String = ""
static func reduce(value: inout String, nextValue: () -> String) {
// Eski qiymatni yangi kelgan qiymat bilan yangilaymiz
value = nextValue()
}
}
Endi uni View da qanday eshitib va yuborish mumkinligini ko'ramiz:
struct PreferenceKeyBootcamp: View {
@State private var parentText: String = "Boshlang'ich Matn"
var body: some View {
VStack {
Text(parentText) // Bu matn tepadagi ota View'da turibdi
.font(.largeTitle)
// Lekin u o'zgarishni ichkaridan kutib oladi
ChildView()
}
// Ichkaridan "CustomTitlePreferenceKey" kelishini kutib eshitib turadi
.onPreferenceChange(CustomTitlePreferenceKey.self) { newTitle in
self.parentText = newTitle
}
}
}
struct ChildView: View {
var body: some View {
Text("Bu child view")
// Shunchaki o'zidan tepaga shu ma'lumotni Preference qilib faryod qiladi!
.preference(key: CustomTitlePreferenceKey.self, value: "Ichkaridan kelgan matn!")
}
}
2-Misol: Ichki elementning o'lchamini (CGSize) Parent'ga uzatish
Eng ko'p ishlatiladigan holat: Bizga ekrandagi biror qutichaning haqiqiy razmeri kerak, va biz uni GeometryReader orqali topib, tepadagi qandaydir elementning ham razmerini unga moslamoqchimiz.
struct RectangleSizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
// ... Qanday ishlatish mumkin:
Rectangle()
.overlay(
GeometryReader { geo in
Color.clear
.preference(key: RectangleSizePreferenceKey.self, value: geo.size)
}
)
Tepadagi Parent (ZStack yoki VStack) dagi biror matn esa huddi shu RectangleSizePreferenceKey ni tinglab o'tirishi (onPreferenceChange) orqali to'g'ridan-to'g'ri o'lchamni olib, o'zini shu razmerga moslab oladi!
3-Misol: ScrollView'da qancha skroll qilinganini (Offset) aniqlash
Eng qiziqarli joyi! Agar siz tepaga aylantirganingizda tepadan NavigationBar tushadigan yoki rasm xiralashadigan qilishni istasangiz, element qayerda joylashganini doimiy o'lchab tepaga uzatishingiz kerak.
struct ScrollOffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
ScrollView ichidagi tepada joylashgan kichik (ko'rinmas) elementga buni beramiz:
Text("Sarlavha")
.background(
GeometryReader { geo in
Color.clear
.preference(key: ScrollOffsetPreferenceKey.self, value: geo.frame(in: .global).minY)
}
)
Endi Parent ScrollView da ushbu .onPreferenceChange(ScrollOffsetPreferenceKey.self) ni ushlab, minY qiymati qancha kichrayib ketganiga qarab (ya'ni tepaga qarab tushyaptimi) o'zingiz xohlagan animatsiya va UI larni ko'rsatishingiz yoki yashirishingiz mumkin bo'ladi!