Semua yang ingin Anda ketahui tentang SwiftUI tetapi takut untuk bertanya


Hai Nama saya Renat, saya sedang mengembangkan layanan analisis berlangganan di iOS - Apphud.


Seperti yang Anda tahu, Apple di WWDC 2019 memperkenalkan kerangka kerja SwiftUI baru, yang dirancang di masa depan untuk menggantikan (atau tidak?) UIKit yang familier. SwiftUI memungkinkan Anda untuk menggambarkan antarmuka aplikasi dalam gaya deklaratif dan sangat mengurangi jumlah kode.


Apple telah memperkenalkan beberapa tutorial bahasa Inggris yang menarik dengan banyak contoh. Saya akan mencoba berbicara tentang kerangka kerja baru dalam bentuk pertanyaan dan jawaban. Jadi ayo pergi.


Sebelum Anda mulai


Untuk bekerja dengan SwiftUI, Anda perlu mengunduh Xcode 11 Beta . Anda juga harus menjadi pengembang Apple terdaftar. Memiliki macOS Catalina terbaru memang diinginkan, tetapi tidak perlu. Tanpanya, Canvas tidak akan tersedia.


Jadi, dalam Xcode 11 Beta, buat proyek baru dan pastikan bahwa "Gunakan SwiftUI" dicentang.


Tanya Jawab


Kemana perginya Interface Builder?


SwiftUI tidak lagi membutuhkan Interface Builder - telah digantikan oleh Canvas , editor antarmuka interaktif yang terkait erat dengan kode. Saat menulis kode, bagian visualnya di kanvas dibuat secara otomatis dan sebaliknya. Sangat nyaman, dan yang paling penting aman. Sekarang aplikasi Anda tidak akan @IBOutlet karena Anda lupa memperbarui @IBOutlet dengan variabel. Pada artikel ini kami tidak akan menyentuh kanvas , kami hanya akan mempertimbangkan kode.


Apakah peluncuran aplikasi berubah?


Ya, sekarang objek awal dalam antarmuka aplikasi bukan UIWindow , tetapi kelas UIScene baru (atau UIWindowScene turunannya). Dan sebuah jendela ditambahkan ke tempat kejadian. Perubahan ini tidak hanya memengaruhi SwiftUI, tetapi iOS 13 secara keseluruhan.


Saat Anda membuat proyek, Anda akan melihat file AppDelegate , SceneDelegate, dan ContentView . SceneDelegate - delegasi dari kelas UIWindowScene , yang digunakan untuk mengontrol adegan dalam aplikasi. Sangat mengingatkan pada AppDelegate .


Kelas SceneDelegate ditentukan dalam Info.plist
Kelas SceneDelegate ditentukan dalam Info.plist


Dalam metode delegasi, scene: willConnectTo: options: membuat jendela dan root UIHostingController yang berisi ContentView . ContentView adalah halaman "rumah" kami. Semua pengembangan akan dilakukan di kelas ini.


Apa bedanya View dengan UIView?


ContentView.swift Anda membuka ContentView.swift , Anda akan melihat deklarasi kontainer ContentView. Seperti yang sudah Anda pahami, tidak ada metode viewDidLoad atau metode viewDidAppear viewDidLoad viewDidAppear . Dasar layar di sini bukanlah UIViewController , melainkan View . Hal pertama yang perlu diperhatikan adalah ContentView adalah struct yang menerima protokol View . Ya, View sekarang View menjadi protokol, dan sangat sederhana. Satu-satunya metode yang perlu Anda terapkan di ContentView adalah mendeskripsikan isi variabel. Semua subview dan tampilan khusus Anda harus menerima protokol View , mis. Harus memiliki variabel body .


Apa itu tubuh?


Body secara langsung adalah wadah kami di mana semua subview lainnya ditambahkan. Ini agak mirip dengan badan di halaman html , di mana halaman html adalah ContentView . Body harus selalu memiliki tepat satu keturunan, dan setiap kelas yang menerima protokol tampilan.


 struct ContentView: View { var body: some View { Text("Hello, world!") } } 

Jenis pengembalian buram atau apa?


Pembangunan some TypeName adalah inovasi Swift 5.1 yang disebut tipe pengembalian buram . Ini digunakan untuk kasus-kasus ketika tidak penting bagi kita objek yang kembali, yang utama adalah bahwa ia mendukung tipe yang ditentukan, dalam hal ini, protokol tampilan.


Jika kita hanya menulis var body: View , maka ini berarti kita harus mengembalikan View . Kelas Any juga tidak berfungsi, karena kita harus melakukan operasi konversi tipe ( menggunakan operator as! ). Oleh karena itu, mereka datang dengan kata khusus some di depan nama protokol untuk menunjukkan jenis pengembalian buram . Alih-alih View kita dapat mengembalikan Text , Image , VStack - apa pun, karena semuanya mendukung protokol View . Tetapi harus ada tepat satu elemen: ketika mencoba mengembalikan lebih dari satu View kompiler akan membuat kesalahan.



Kesalahan kompilasi ketika mencoba mengembalikan lebih dari satu elemen ke tubuh


Apa sintaks di dalam tanda kurung dan di mana addSubview?


Swift 5.1 memperkenalkan kemampuan untuk mengelompokkan objek menjadi satu kesatuan dalam gaya deklaratif. Ini mirip dengan larik di dalam blok penutup , tetapi elemen-elemennya disebutkan dari baris baru tanpa koma dan kembali . Mekanisme ini disebut Function Builder .


Ini banyak digunakan di SwiftUI. Berdasarkan Function Builder, mereka membuat ViewBuilder - desainer antarmuka deklaratif. Menggunakan ViewBuilder kita tidak perlu lagi menulis addSubview untuk setiap elemen - cukup daftarkan semua View dari baris baru di dalam blok closure . SwiftUI akan menambahkan dan mengelompokkan elemen ke dalam wadah induk yang lebih kompleks.


 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @_functionBuilder public struct ViewBuilder { /// Builds an empty view from an block containing no statements, `{ }`. public static func buildBlock() -> EmptyView /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through /// unmodified. public static func buildBlock<Content>(_ content: Content) -> Content where Content : View } 

Iklan ViewBuilder dalam kerangka kerja SwiftUI


Bagaimana cara menambahkan UILabel, UIImageView dan elemen lainnya?


Elemen dibuat dengan sangat sederhana: setiap View perlu ditulis dari baris baru dan mengubah tampilan menggunakan fungsi pengubah ( view modifiers ). Perbedaan antara pengubah dan fungsi yang akrab bagi kita adalah bahwa mereka selalu mengembalikan objek wadah bukannya void . Oleh karena itu, kita dapat membuat seluruh rantai pengubah melalui titik ini.


 var body: some View { VStack{ Text("World Time").font(.system(size: 30)) Text("Yet another subtitle").font(.system(size: 20)) } } 

Namun, tidak semua kontrol dan Lihat memiliki analog mereka di SwiftUI. Berikut adalah sebagian daftar kelas dari UIKit dan analognya:


  • UITableView -> List


  • UICollectionView tidak memiliki analog


  • UILabel -> Text


  • UITextField -> TextField


  • UIImageView -> Image


  • UINavigationController -> NavigationView


  • UIButton -> Button


  • UIStackView -> HStack / VStack


  • UISwitch -> Toggle


  • UISlider -> Slider


  • UITextView tidak memiliki analog


  • UIAlertController -> Alert / ActionSheet


  • UISegmentedControl -> SegmentedControl


  • UIStepper -> Stepper


  • UIDatePicker -> DatePicker



Bagaimana navigasi antar layar?


Pengontrol navigasi berperan sebagai NavigationView khusus. Bungkus saja kode Anda di NavigationView{} . Dan tindakan transisi itu sendiri dapat ditambahkan ke tombol NavigationLink khusus, yang mendorong layar DetailView bersyarat.


 var body: some View { NavigationView { Text("World Time").font(.system(size: 30)) NavigationLink(destination: DetailView() { Text("Go Detail") } } } 

Bagaimana cara menyajikan tampilan baru secara modern? Ini dilakukan, misalnya, menggunakan lembar konstruksi:


 Button(action: { print("Button Pushed") self.show_modal = true }) { Text("Present Modal") }.sheet(isPresented: self.$show_modal) { ModalView() } 

Seperti yang disebutkan di atas, body dapat mengembalikan tidak hanya instance View , tetapi juga kelas lain yang menerima protokol ini. Ini memberi kita kesempatan untuk DetailView bukan DetailView , tetapi bahkan Text atau Image !


Bagaimana cara mengatur elemen di layar?


Elemen disusun secara terpisah satu sama lain dan dapat ditempatkan secara vertikal di dalam VStack , secara horizontal HStack dan satu di atas ZStack lainnya. ScrollView dan ListView juga tersedia bagi kami. Anda dapat mengganti dan berbagi wadah ini untuk mendapatkan elemen elemen apa pun.


Dengan menggabungkan wadah satu sama lain, Anda bisa mendapatkan pohon yang cukup besar dengan banyak lampiran. Namun, SwiftUI dioptimalkan secara khusus untuk hal ini, sehingga penumpukan kontainer yang dalam tidak mempengaruhi kinerja. Ini dinyatakan dalam video dengan wwdc ( mulai 15:32 ).


 var body: some View { NavigationView { VStack { NavigationLink(destination: LargeView(timeString: subtitle)) { Text("See Fullscreen") } Text("World Time").font(.system(size: 30)) } } } 

Bagaimana cara menampilkan Bilah Navigasi?


Mendeklarasikan NavigationView tidak cukup, Anda harus menentukan judul dan gaya navigasi untuk bilah navigasi .


 NavigationView { VStack{}.navigationBarTitle(Text("World Time"), displayMode: .inline) } 

Perhatikan bahwa fungsi navigationBarTitle tidak dipanggil pada NavigationView , tetapi pada tampilan internalnya. DisplayMode adalah parameter yang menunjukkan gaya bilah navigasi : besar atau standar.


Apakah ada analog dari metode viewDidLoad?


Jika Anda ingin mengeksekusi kode ketika menginisialisasi View , Anda dapat melakukan ini dengan menambahkan fungsi onAppear {}. OnAppear dapat ditambahkan ke View apa pun, misalnya, ke VStack . Dalam contoh ini, ketika wadah muncul di layar, permintaan http dibuat ke server.


 struct ContentView : View { @State var statusString : String = "World Time" var body: some View { NavigationView { VStack { NavigationLink(destination:DetailView()) { Text("Go Detail") } Text(statusString).font(.system(size: 30)) }.onAppear { self.loadTime() } } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date())" } } } } 

Kami memanggil fungsi loadTime , yang meminta waktu saat ini dari server dan mengembalikan model WorldTime . Kami tidak akan pergi dalam siklus di kelas NetworkService , Anda dapat melihat semua kode, memiliki kode sumber yang diunduh. Tautan di akhir artikel.

Variabel var statusString diberikan untuk menetapkan waktu saat ini nanti. Variabel memiliki atribut khusus @State . Apa yang dia maksud


Pembungkus Properti atau apa itu @State ?


Swift 5.1 memperkenalkan apa yang disebut pembungkus properti (atau delegasi properti ). Dalam pembungkus properti SwiftUI digunakan untuk memperbarui atau mengikat salah satu parameter tampilan dengan variabel kita sendiri, misalnya, nilai sakelar Toggle .


Atribut @State adalah atribut khusus yang ditempatkan sebelum deklarasi variabel. Ini memungkinkan kami untuk melacak perubahan properti secara otomatis tanpa kode tambahan. Pada contoh di atas, teks "Waktu Dunia" akan berubah ke tanggal saat ini segera setelah kami memperbarui nilai statusString.


Untuk mengikat nilai ( Properties Binding ), kita dapat menentukan karakter khusus $ sebelum nama variabel dalam kode itu sendiri:


 struct DetailsView: View { @State var changeToggle: Bool var body: some View { Toggle(isOn: $changeToggle) { Text("Change Toggle") } } } 

Dengan mengubah posisi sakelar, nilai variabel juga akan berubah.


Pembungkus properti adalah komponen yang sangat penting dari SwiftUI, saya hanya menyebutkannya secara sepintas. Untuk kenalan yang lebih terperinci dengan pembungkus properti, tonton video dari wwdc di sini (dari menit ke-37), di sini (dari ke-12) dan di sini (dari ke-19).


Bagaimana cara menambahkan tampilan ke runtime?


Perlu segera dicatat bahwa Anda tidak dapat menambahkan tampilan kapan saja dalam arti kata sebenarnya. SwiftUI adalah kerangka kerja deklaratif yang menjadikan seluruh tampilan . Namun, Anda dapat mengatur berbagai kondisi di dalam tubuh dan memperbarui keadaan tampilan ketika mereka berubah. Dalam contoh ini, kami menggunakan banyak @State – if paling sederhana @State – if dengan variabel isTimeLoaded .


 struct ContentView : View { @State var statusString : String = "World Time" @State var isTimeLoaded : Bool = false var body: some View { NavigationView { VStack { if isTimeLoaded { addNavigationLink() } Text(statusString).font(.system(size: 30)).lineLimit(nil) }.navigationBarTitle(Text("World Time"), displayMode: .inline) }.onAppear { self.loadTime() } } func addNavigationLink() -> some View { NavigationLink(destination: Text("124!!!")) { Text("Go Detail") } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date().description(with: Locale.current))" self.isTimeLoaded = true } } } } struct DetailView : View { var timeString : String var body : some View { Text(timeString).font(.system(size: 40)).lineLimit(nil) } } 

Omong-omong, apakah Anda memperhatikan bahwa fungsi addNavigationLink() tidak memiliki kata return ? Ini adalah inovasi lain dari Swift 5.1 - untuk fungsi dengan satu ekspresi, sekarang opsional untuk menulis return . Tapi kamu bisa menulis.


Kesimpulan


Ini hanya bagian dari Tanya Jawab SwiftUI Saya memeriksa masalah umum, saya harap artikel ini akan membantu pemula memahami poin utama dari kerangka ini. SwiftUI masih mentah, tetapi tidak diragukan lagi akan ditingkatkan.


Pertanyaan logisnya adalah: apakah itu layak untuk dipelajari oleh UIKit? Tentu saja ya UIKit adalah dasar pemrograman pada iOS dan akan dikembangkan lebih lanjut. Selain itu, banyak komponen SwiftUI adalah pembungkus di atas UIKit. Nah, sejauh ini tidak ada perpustakaan, kerangka kerja, pod untuk SwiftUI. Semuanya harus ditulis sendiri. Jadi lebih baik untuk mempelajari kedua pendekatan pengembangan - sehingga Anda akan menjadi pengembang yang lebih berharga.


Anda dapat mengunduh sumber proyek di sini .


Terima kasih telah membaca artikel sampai akhir. Semoga bermanfaat.


Apa yang harus dibaca?


Source: https://habr.com/ru/post/id455970/


All Articles