Alles, was Sie über SwiftUI wissen wollten, aber Angst hatten zu fragen


Hallo! Mein Name ist Renat. Ich entwickle einen Abonnement-Analysedienst in iOS - Apphud.


Wie Sie wissen, hat Apple auf der WWDC 2019 sein neues SwiftUI-Framework vorgestellt, das in Zukunft das bekannte UIKit ersetzen soll (oder nicht?). Mit SwiftUI können Sie die Anwendungsoberfläche in einem deklarativen Stil beschreiben und die Codemenge erheblich reduzieren.


Apple hat bereits einige interessante englische Tutorials mit vielen Beispielen vorgestellt. Ich werde versuchen, in Form von Fragen und Antworten über das neue Framework zu sprechen. Also lass uns gehen.


Bevor Sie beginnen


Um mit SwiftUI arbeiten zu können, müssen Sie Xcode 11 Beta herunterladen. Sie müssen außerdem ein registrierter Apple-Entwickler sein. Das neueste macOS Catalina zu haben ist wünschenswert, aber nicht notwendig. Ohne diese Option ist Canvas nicht verfügbar.


Erstellen Sie in Xcode 11 Beta ein neues Projekt und stellen Sie sicher, dass "SwiftUI verwenden" aktiviert ist.


Fragen und Antworten


Wohin ist Interface Builder gegangen?


SwiftUI benötigt Interface Builder nicht mehr - es wurde durch Canvas ersetzt , einen interaktiven Schnittstelleneditor, der eng mit Code verwandt ist. Beim Schreiben von Code wird sein visueller Teil in der Zeichenfläche automatisch generiert und umgekehrt. Sehr praktisch und vor allem sicher. Jetzt @IBOutlet Ihre Anwendung nicht ab, da Sie vergessen haben, die @IBOutlet mit der Variablen zu aktualisieren. In diesem Artikel werden wir keine Leinwand berühren, sondern nur den Code betrachten.


Hat sich der Anwendungsstart geändert?


Ja, jetzt ist das ursprüngliche Objekt in der Anwendungsschnittstelle nicht UIWindow , sondern die neue UIScene Klasse (oder deren Nachkomme UIWindowScene ). Und der Szene wird ein Fenster hinzugefügt. Diese Änderungen betreffen nicht nur SwiftUI, sondern iOS 13 insgesamt.


Wenn Sie ein Projekt erstellen, werden die Dateien AppDelegate , SceneDelegate und ContentView angezeigt . SceneDelegate - Ein Delegat der UIWindowScene Klasse, mit dem Szenen in der Anwendung gesteuert werden. Erinnert stark an AppDelegate .


Die SceneDelegate-Klasse wird in Info.plist angegeben
Die SceneDelegate-Klasse wird in Info.plist angegeben


In der Delegate-Methode erstellt scene: willConnectTo: options: ein Fenster und einen Root- UIHostingController , der eine ContentView enthält. ContentView ist unsere "Homepage". Alle Entwicklungen werden in dieser Klasse durchgeführt.


Wie unterscheidet sich View von UIView?


ContentView.swift Sie ContentView.swift öffnen, wird eine ContentView-Containerdeklaration angezeigt. Wie Sie bereits verstanden haben, gibt es viewDidLoad viewDidAppear keine bekannten viewDidLoad oder viewDidAppear Methoden. Die Basis der Bildschirme ist hier nicht der UIViewController , sondern die Ansicht . Als erstes fällt auf, dass ContentView eine struct , die das View Protokoll akzeptiert. Ja, View jetzt ein Protokoll und sehr einfach. Die einzige Methode, die Sie in ContentView implementieren ContentView ist die Beschreibung des Variablenkörpers. Alle Ihre Unteransichten und benutzerdefinierten Ansichten müssen das View Protokoll akzeptieren, d. H. Eine body Variable haben.


Was ist Körper?


Body ist direkt unser Container, in dem alle anderen Unteransichten hinzugefügt werden. Dies ähnelt in gewisser Weise dem ContentView einer HTML- Seite, bei der es sich bei der HTML- Seite um eine ContentView . Body sollte immer genau einen Nachkommen und jede Klasse haben, die das View Protokoll akzeptiert.


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

Undurchsichtige Rückgabetypen oder welche?


Die Konstruktion some TypeName ist eine Swift 5.1-Innovation, die als undurchsichtiger Rückgabetyp bezeichnet wird . Es wird für Fälle verwendet, in denen es für uns nicht wichtig ist, welches Objekt zurückgegeben werden soll. Hauptsache, es unterstützt den angegebenen Typ, in diesem Fall das View Protokoll.


Wenn wir einfach var body: View schreiben würden, würde dies bedeuten, dass wir die View . Die Any Klasse funktioniert auch nicht, da wir eine Typkonvertierungsoperation ausführen müssten (unter Verwendung des Operators as! ). Daher haben sie das spezielle Wort some vor dem Protokollnamen gefunden, um den undurchsichtigen Rückgabetyp anzuzeigen. Anstelle von View wir Text , Image , VStack - alles, da alle das View Protokoll unterstützen. Es muss jedoch genau ein Element geben: Wenn versucht wird, mehr als eine View Compiler einen Fehler aus.



Kompilierungsfehler beim Versuch, mehr als ein Element an body zurückzugeben


Wie lautet die Syntax in den Klammern und wo befindet sich addSubview?


Mit Swift 5.1 wurde die Möglichkeit eingeführt, Objekte in einem deklarativen Stil zu einem Ganzen zu gruppieren. Dies ähnelt einem Array innerhalb eines Abschlussblocks , die Elemente werden jedoch aus einer neuen Zeile ohne Kommas und Rückgabe aufgelistet. Dieser Mechanismus wurde Function Builder genannt .


Dies ist in SwiftUI weit verbreitet. Basierend auf Function Builder haben sie ViewBuilder - einen deklarativen Schnittstellendesigner - erstellt. Mit ViewBuilder wir nicht mehr für jedes Element addSubview schreiben - listen Sie einfach alle addSubview aus einer neuen Zeile im addSubview auf. SwiftUI fügt Elemente zu einem komplexeren übergeordneten Container hinzu und gruppiert sie.


 @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 } 

ViewBuilder-Anzeige im SwiftUI-Framework


Wie füge ich UILabel, UIImageView und andere Elemente hinzu?


Elemente werden sehr einfach erstellt: Jede View muss aus einer neuen Zeile geschrieben werden und das Erscheinungsbild mithilfe der Modifikatorfunktionen ( Ansichtsmodifikatoren ) ändern. Der Unterschied zwischen uns bekannten Modifikatoren und Funktionen besteht darin, dass sie immer ein Containerobjekt anstelle von void . Daher können wir durch den Punkt ganze Ketten von Modifikatoren erstellen.


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

Allerdings haben nicht alle Steuerelemente und View ihre Analoga in SwiftUI. Hier ist eine unvollständige Liste der Klassen von UIKit und ihrer Analoga:


  • UITableView -> List


  • UICollectionView hat kein Analogon


  • UILabel -> Text


  • UITextField -> TextField


  • UIImageView -> Image


  • UINavigationController -> NavigationView


  • UIButton -> Button


  • UIStackView -> HStack / VStack


  • UISwitch -> Toggle


  • UISlider -> Slider


  • UITextView hat kein Analogon


  • UIAlertController -> Alert / ActionSheet


  • UISegmentedControl -> SegmentedControl


  • UIStepper -> Stepper


  • UIDatePicker -> DatePicker



Wie ist die Navigation zwischen den Bildschirmen?


Der Navigationscontroller übernimmt die Rolle einer speziellen NavigationView . Wickeln Sie einfach Ihren Code in NavigationView{} . Die Übergangsaktion selbst kann zu einer speziellen NavigationLink Schaltfläche hinzugefügt werden, die den bedingten DetailView Bildschirm DetailView .


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

Wie kann man neue Ansichten modal präsentieren? Dies geschieht beispielsweise mit dem Blattkonstrukt :


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

Wie oben erwähnt, kann body nicht nur eine Instanz von View , sondern auch jede andere Klasse, die dieses Protokoll akzeptiert. Dies gibt uns die Möglichkeit DetailView nicht DetailView , sondern sogar Text oder Image zu DetailView !


Wie ordne ich Elemente auf dem Bildschirm an?


Die Elemente sind unabhängig voneinander angeordnet und können vertikal innerhalb von VStack , horizontal HStack und übereinander ZStack . ScrollView und ListView stehen uns ebenfalls zur Verfügung. Sie können diese Container abwechseln und freigeben, um ein beliebiges Netz von Elementen zu erhalten.


Wenn Sie Container miteinander kombinieren, erhalten Sie einen ziemlich großen Baum mit einer großen Anzahl von Anhängen. SwiftUI ist jedoch speziell dafür optimiert, sodass eine tiefe Verschachtelung der Container die Leistung nicht beeinträchtigt. Dies wird im Video mit wwdc angegeben ( ab 15:32 Uhr ).


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

Wie wird die Navigationsleiste angezeigt?


Das Deklarieren einer NavigationView reicht nicht aus. Sie müssen einen Navigationstitel und -stil für die Navigationsleiste angeben.


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

Beachten Sie, dass die Funktion navigationBarTitle nicht in der NavigationView , sondern in der internen View aufgerufen wird. DisplayMode ist ein Parameter, der den Stil der Navigationsleiste angibt: groß oder Standard.


Gibt es ein Analogon zur viewDidLoad-Methode?


Wenn Sie beim Initialisieren der View Code ausführen möchten, können Sie dies tun, indem Sie die Funktion onAppear {} hinzufügen. OnAppear kann zu jeder View hinzugefügt werden, z. B. zu VStack . In diesem Beispiel wird eine http-Anforderung an den Server gesendet, wenn der Container auf dem Bildschirm angezeigt wird.


 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())" } } } } 

Wir rufen die Funktion loadTime , die die aktuelle Uhrzeit vom Server anfordert und das WorldTime Modell zurückgibt. Wir werden in der NetworkService Klasse keine Zyklen durchführen. Sie können sich den gesamten Code ansehen, nachdem Sie die Quellcodes heruntergeladen haben. Link am Ende des Artikels.

Die Variable var statusString wurde gerendert, um ihr später die aktuelle Zeit zuzuweisen. Die Variable hat ein spezielles Attribut @State . Was meint er


Property Wrapper oder was ist @State ?


Swift 5.1 führte die sogenannten Property Wrapper (oder Property Delegates ) ein. In SwiftUI- Eigenschaften werden Wrapper verwendet, um einen der Ansichtsparameter mit unserer eigenen Variablen zu aktualisieren oder zu binden, z. B. den Wert des Umschalters.


Das @State Attribut ist ein spezielles Attribut, das vor einer Variablendeklaration steht. Auf diese Weise können wir Eigenschaftsänderungen ohne zusätzlichen Code automatisch verfolgen. Im obigen Beispiel ändert sich der Text "Weltzeit" auf das aktuelle Datum, sobald wir den statusString-Wert aktualisieren.


Um Werte zu binden ( Eigenschaftenbindung ), können wir im Code selbst ein Sonderzeichen $ vor dem Namen der Variablen angeben:


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

Durch Ändern der Schalterposition ändert sich auch der Wert der Variablen.


Property Wrapper sind ein sehr wichtiger Bestandteil von SwiftUI, ich habe sie nur beiläufig erwähnt. Für eine detailliertere Bekanntschaft mit Property Wrappern sehen Sie sich das Video von wwdc hier (ab der 37. Minute), hier (ab der 12. Minute) und hier (ab der 19. Minute) an.


Wie füge ich der Laufzeit eine Ansicht hinzu?


Es ist sofort erwähnenswert, dass Sie zu keinem Zeitpunkt eine Ansicht im wörtlichen Sinne des Wortes hinzufügen können. SwiftUI ist ein deklaratives Framework, das die gesamte Ansicht rendert. Sie können jedoch verschiedene Bedingungen im Körper festlegen und den Status der Ansicht aktualisieren, wenn sie sich ändern. In diesem Beispiel verwenden wir die einfachste Gruppe von @State – if mit der Variablen 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) } } 

addNavigationLink() Sie übrigens bemerkt, dass die Funktion addNavigationLink() nicht das Wort return ? Dies ist eine weitere Neuerung von Swift 5.1 - für Funktionen mit einem einzelnen Ausdruck ist es jetzt optional, return . Aber du kannst schreiben.


Fazit


Dies ist nur ein Teil der SwiftUI-Fragen und Antworten. Ich habe allgemeine Probleme untersucht und hoffe, dass dieser Artikel Anfängern hilft, die wichtigsten Punkte dieses Frameworks zu verstehen. SwiftUI ist noch roh, wird aber zweifellos verbessert.


Die logische Frage ist: Lohnt es sich, UIKit zu lernen? Natürlich ja . UIKit ist die Basis für die Programmierung unter iOS und wird weiterentwickelt. Darüber hinaus sind viele SwiftUI-Komponenten ein Wrapper über UIKit. Bisher gibt es keine Bibliotheken, Frameworks und Pods für SwiftUI. Alles muss von dir selbst geschrieben werden. Es ist daher besser, beide Entwicklungsansätze zu untersuchen, damit Sie ein wertvollerer Entwickler sind.


Sie können die Projektquellen hier herunterladen.


Vielen Dank, dass Sie den Artikel bis zum Ende gelesen haben. Ich hoffe, Sie finden es nützlich.


Was zu lesen?


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


All Articles