- Published on
SwiftUI-da Sheet orqali Location Detail View yaratish
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
LocationDetailView β yangi fayl
Navigator-da o'ng tugmani bosib, Views papkasida yangi SwiftUI View fayl yaratamiz β unga LocationDetailView deb nom beramiz.
Bu view β ma'lum bir location haqidagi barcha tafsilotlarni ko'rsatadigan ekran. Shuning uchun, bu view-ni yaratganda, unga location uzatilishi kerak:
struct LocationDetailView: View {
let location: Location
var body: some View {
Text(location.name)
}
}
Preview-ni to'g'rilash uchun, LocationsDataService-dan birinchi locationni olamiz (preview uchun ! bilan explicit unwrap qilamiz β haqiqiy kodda bunday qilmang):
#Preview {
LocationDetailView(location: LocationsDataService.locations.first!)
.environmentObject(LocationsViewModel())
}
ScrollView va asosiy tuzilma
Bu ekranning hammasi ScrollView ichida bo'ladi β chunki ko'p kontent bor va ekranga sig'masligi mumkin:
var body: some View {
ScrollView {
VStack {
imageSection
.shadow(color: .black.opacity(0.3), radius: 20, x: 0, y: 10)
VStack(alignment: .leading, spacing: 16) {
titleSection
Divider()
descriptionSection
Divider()
mapLayer
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
}
.ignoresSafeArea()
.overlay(alignment: .topLeading) {
backButton
}
.background(.ultraThinMaterial)
}
imageSection β rasm karuseli
Bir nechta rasmni swipe qilib ko'rish uchun TabView va PageTabViewStyle-dan foydalanamiz:
private var imageSection: some View {
TabView {
ForEach(location.imageNames, id: \.self) { name in
Image(name)
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.width)
.clipped()
}
}
.frame(height: 500)
.tabViewStyle(.page)
}
Muhim: .frame(width: UIScreen.main.bounds.width) va .clipped() β keyingi rasm oldingisinining ustiga chiqib kelishining oldini oladi. Agar bu bo'lmasa, swiping paytida g'alati vizual effekt paydo bo'ladi.
Eslatma: iPad uchun moslashtirilganda, bu qatirni keyinroq o'zgartirishimiz kerak bo'ladi, chunki iPad-da bu ekran to'liq kenglikda bo'lmaydi.
titleSection
private var titleSection: some View {
VStack(alignment: .leading, spacing: 8) {
Text(location.name)
.font(.largeTitle)
.fontWeight(.semibold)
Text(location.cityName)
.font(.title3)
.foregroundColor(.secondary)
}
}
Muhim: VStack-ni to'liq kenglikka yoyish uchun .frame(maxWidth: .infinity, alignment: .leading) tashqi VStack-ga qo'llaniladi β aks holda VStack faqat kontent kengligida qoladi.
descriptionSection
private var descriptionSection: some View {
VStack(alignment: .leading, spacing: 16) {
Text(location.description)
.font(.subheadline)
.foregroundColor(.secondary)
if let url = URL(string: location.link) {
Link("Read more on Wikipedia", destination: url)
.font(.headline)
.tint(.blue)
}
}
}
Muhim: URL(string:) optional qaytaradi, shuning uchun if let bilan xavfsiz unwrap qilish kerak. tint(.blue) β havolalar an'anaviy ko'k rangda ko'rinishi uchun (accent color-ga bog'liq bo'lmasligi uchun).
mapLayer β kichik xarita
Location detail ekranida xuddi shu locationning ustida kichik, interaktiv bo'lmagan xarita ko'rsatiladi:
@EnvironmentObject private var vm: LocationsViewModel
private var mapLayer: some View {
Map(coordinateRegion: .constant(MKCoordinateRegion(
center: location.coordinates,
span: vm.mapSpan
)),
annotationItems: [location]) { location in
MapAnnotation(coordinate: location.coordinates) {
LocationMapAnnotationView()
.shadow(radius: 10)
}
}
.aspectRatio(1, contentMode: .fit)
.cornerRadius(30)
.allowsHitTesting(false)
}
Muhim jihatlar:
.constant(...)β bu xarita hech qachon o'zgarmaydi, shuning uchun@Stateyoki@Bindingshart emas.MKCoordinateRegiondoimiy qiymat sifatida uzatiladi.annotationItems: [location]β butun locationlar ro'yxati emas, faqat joriy location β bitta pin ko'rsatiladi..aspectRatio(1, contentMode: .fit)β xaritani kvadrat shaklida ko'rsatish uchun..allowsHitTesting(false)β xaritani touch-dan himoya qiladi. Sheet scroll bo'ladi, ScrollView scroll bo'ladi, xarita ham scroll bo'lsa β foydalanuvchi uchun chalkash bo'lar edi. Shuning uchun xaritani "qotib qo'yamiz".import MapKitβ fayl boshida bo'lishi shart.
backButton va material background
Sheet-ni yopish uchun back tugmasi kerak. U scroll view-ning ustida, yuqori chap qismida joylashadi:
private var backButton: some View {
Button {
vm.sheetLocation = nil
} label: {
Image(systemName: "xmark")
.font(.headline)
.padding(16)
.foregroundColor(.primary)
.background(.thickMaterial)
.cornerRadius(10)
.shadow(radius: 4)
.padding()
}
}
vm.sheetLocation = nilβ qiymatni nil-ga o'rnatish sheet-ni avtomatik yopadi..thickMaterialβ oq fonga o'xshash, ammo ortidagi kontentni biroz ko'rsatuvchi material.
Butun scroll view-ga ham biroz fon kerak:
.background(.ultraThinMaterial)
ViewModel-ga sheetLocation qo'shish
LocationsViewModel-ga location detail sheet-ini boshqaradigan o'zgaruvchi qo'shamiz:
// Show location detail via sheet
@Published var sheetLocation: Location? = nil
Location?β optional, chunkinil= sheet yopiq, qiymat bor = sheet ochiq.- Boshlang'ich qiymat β
nil(ilova ochilganda sheet ko'rinmaydi).
LocationsView-da sheet qo'shish
LocationsView-dagi asosiy ZStack-ga .sheet modifikatorini qo'shamiz:
.sheet(item: $vm.sheetLocation) { location in
LocationDetailView(location: location)
}
.sheet(item:)βitemoptional identifiable ob'ektga bog'lanadi. Qiymatnilbo'lmagan zahoti, sheet avtomatik ochiladi. Sheet yopilganda qiymat avtomatiknilbo'ladi.
LocationPreviewView-da "Learn More" tugmasi
LocationPreviewView-dagi "Learn more" tugmasida:
Button("Learn more") {
vm.sheetLocation = location
}
Bu qiymatni o'rnatish β sheet-ni ochish uchun yetarli.
Xaritadagi MKCoordinateSpan
Location detail view-dagi xarita biroz ko'proq zoom qilingan bo'lishi kerak. Buning uchun LocationsViewModel-dagi mapSpan-ni yangilaymiz yoki alohida span yaratamiz:
// LocationDetailView ichida mapLayer-da:
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
0.01 β kichik span, ya'ni xarita locationga yaqinroq zoom qilingan bo'ladi.
Natija
Endi ilova to'liq ishlaydi:
- Xaritada location-ni bosamiz β LocationPreviewView paydo bo'ladi
- "Learn more" tugmasini bosamiz β LocationDetailView sheet sifatida pastdan yuqoriga chiqadi
- Sheet ichida:
- Rasmlar karuseli (swipe qilib o'tish mumkin)
- Location nomi va shahri
- Tavsif matni
- Wikipedia havolasi
- Kichik, interaktiv bo'lmagan xarita (bitta pin bilan)
- X tugmasi yoki pastga swipe qilish β sheet yopiladi
Barcha ma'lumot LocationsViewModel orqali boshqariladi β yangi ekran ham xuddi shu @EnvironmentObject-dan foydalanadi, shuning uchun hamma narsa doim sinxron.