Swift from Scratch: Vererbung und Protokolle
() translation by (you can also view the original English article)
Wenn Sie die vorherigen Artikel dieser Serie gelesen haben, sollten Sie die Grundlagen der Swift-Programmiersprache inzwischen gut verstehen. Wir haben über Variablen, Konstanten und Funktionen gesprochen, und im vorherigen Artikel haben wir die Grundlagen der objektorientierten Programmierung in Swift behandelt.
Während Spielplätze ein großartiges Werkzeug sind, um mit Swift zu spielen und die Sprache zu lernen, ist es Zeit, weiter zu machen und unser erstes Swift-Projekt in Xcode zu erstellen. In diesem Artikel werden wir die Grundlagen einer einfachen Aufgabenanwendung mit Xcode und Swift erstellen. Nebenbei lernen wir mehr über objektorientierte Programmierung und schauen uns auch die Integration zwischen Swift und Objective-C genauer an.
Voraussetzungen
Wenn Sie mir folgen möchten, stellen Sie sicher, dass Xcode 6.3 oder höher auf Ihrem Computer installiert ist. Zum Zeitpunkt der Erstellung dieses Artikels befindet sich Xcode 6.3 in der Betaversion und ist im Apple iOS Dev Center für registrierte iOS-Entwickler erhältlich.
Der Grund dafür, Xcode 6.3 oder höher zu verlangen, besteht darin, Swift 1.2 nutzen zu können, das Apple vor einigen Tagen eingeführt hat. Swift 1.2 führt eine Reihe von großartigen Ergänzungen ein, die wir im Rest dieser Serie nutzen werden.
1. Projekteinrichtung
Schritt 1: Wählen Sie eine Vorlage
Starten Sie Xcode 6.3 oder höher und wählen Sie Neu > Projekt ... im Menü Datei. Wählen Sie aus der Liste der iOS-Anwendungsvorlagen die Vorlage Einzelansichtsanwendung und klicken Sie auf Weiter.



Schritt 2: Konfigurieren Sie das Projekt
Benennen Sie das Projekt ToDo und legen Sie Sprache auf Swift fest. Stellen Sie sicher, dass Geräte auf iPhone eingestellt ist und das Kontrollkästchen Core-Daten verwenden deaktiviert ist. Klicke weiter um fortzufahren.



Sagen Sie Xcode, wo Sie Ihr Projekt speichern möchten, und klicken Sie rechts unten auf Erstellen, um Ihr erstes Swift-Projekt zu erstellen.
2. Projekt Anatomie
Bevor wir unsere Reise nach Swift fortsetzen, nehmen wir uns einen Moment Zeit, um zu sehen, was Xcode für uns geschaffen hat. Wenn Sie mit der Entwicklung von Xcode und iOS noch nicht vertraut sind, wird Ihnen das meiste neu sein. Durch die Arbeit mit einem Swift-Projekt erhalten Sie jedoch ein besseres Gefühl dafür, wie Klassen und Strukturen in Swift aussehen und sich verhalten.
Die Projektvorlage unterscheidet sich nicht wesentlich von einem Projekt, das mit Objective-C als Programmiersprache erstellt wurde. Die wichtigsten Unterschiede beziehen sich auf die Klassen AppDelegate
und ViewController
.
Wenn Sie in der Vergangenheit mit Objective-C gearbeitet haben, werden Sie feststellen, dass die Projektvorlage weniger Dateien enthält. In unserem Projekt befinden sich keine Header- (.h) oder Implementierungsdateien (.m). In Swift haben Klassen keine separate Header-Datei, in der die Schnittstelle deklariert ist. Stattdessen wird eine Klasse deklariert und in einer einzigen .swift-Datei implementiert.
Unser Projekt enthält derzeit zwei Swift-Dateien, eine für die AppDelegate
-Klasse, AppDelegate.swift, und eine weitere für die ViewController
-Klasse, ViewController.swift. Das Projekt enthält außerdem ein Storyboard, ein Main.Storyboard und eine XIB-Datei für den Startbildschirm LaunchScreen.xib. Wir werden ein wenig später in diesem Artikel mit dem Storyboard arbeiten. Es enthält derzeit nur die Szene für die ViewController
-Klasse.
Es gibt eine Reihe anderer Dateien und Ordner, die im Projekt enthalten sind, aber wir werden diese für jetzt ignorieren. Sie spielen keine wichtige Rolle im Rahmen dieses Artikels.
3. Vererbung
Das erste, was wir in diesem Artikel behandeln, ist die Vererbung, ein gängiges Paradigma der objektorientierten Programmierung. In Swift können nur Klassen von einer anderen Klasse erben. Mit anderen Worten, Strukturen und Aufzählungen unterstützen keine Vererbung. Dies ist einer der Hauptunterschiede zwischen Klassen und Strukturen.
Öffnen Sie ViewController.swift, um die Vererbung in Aktion zu sehen. Die Benutzeroberfläche und die Implementierung der ViewController
-Klasse ist ziemlich einfach, was es uns leichter macht, uns auf das Wesentliche zu konzentrieren.
UIKit
An der Spitze von ViewController.swift sollten Sie eine Import-Anweisung für das UIKit-Framework sehen. Denken Sie daran, dass das UIKit-Framework die Infrastruktur zum Erstellen einer funktionalen iOS-Anwendung bereitstellt. Die Importanweisung oben stellt uns diese Infrastruktur in ViewController.swift zur Verfügung.
1 |
import UIKit |
Oberklasse
Unterhalb der import-Anweisung definieren wir eine neue Klasse namens ViewController. Der Doppelpunkt, nach dem der Klassenname nicht übersetzt wird, ist vom Typ, wie wir zuvor in dieser Serie gesehen haben. Stattdessen ist die Klasse nach dem Doppelpunkt die Oberklasse der ViewController
-Klasse. Mit anderen Worten, das folgende Snippet könnte gelesen werden, wenn wir eine Klasse namens ViewController
definieren, die von UIViewController
erbt.
1 |
class ViewController: UIViewController { |
2 |
|
3 |
}
|
Dies erklärt auch das Vorhandensein der Import-Anweisung oben in ViewController.swift, da die UIViewController
-Klasse im UIKit-Framework definiert ist.
Überschreibungen
Die ViewController
-Klasse enthält derzeit zwei Methoden. Beachten Sie jedoch, dass jeder Methode das Schlüsselwort override
vorangestellt ist. Dies zeigt an, dass die Methoden in der Superklasse der Klasse - oder höher in der Vererbungsstruktur - definiert und von der ViewController
-Klasse überschrieben werden.
1 |
class ViewController: UIViewController { |
2 |
|
3 |
override func viewDidLoad() { |
4 |
super.viewDidLoad() |
5 |
}
|
6 |
|
7 |
override func didReceiveMemoryWarning() { |
8 |
super.didReceiveMemoryWarning() |
9 |
}
|
10 |
}
|
Das override
konstrukt existiert nicht in Objective-C. In Objective-C implementieren Sie eine überschriebene Methode in einer Unterklasse, ohne explizit anzugeben, dass sie eine Methode oberhalb der Vererbungsstruktur überschreibt. Die Objective-C-Laufzeit kümmert sich um den Rest.
Das gleiche gilt für Swift, aber die Override
-Schlüsselwörter fügen dem Override der Methode Sicherheit hinzu. Da wir der Methode viewDidLoad
das Schlüsselwort override
vorangestellt haben, erwartet Swift diese Methode in der Oberklasse der Klasse oder höher im Vererbungsbaum. Einfach gesagt, wenn Sie eine Methode überschreiben, die nicht in der Vererbungsstruktur existiert, wird Swift einen Fehler auslösen. Sie können dies testen, indem Sie die Methode viewDidLoad
falsch schreiben, wie unten gezeigt.



4. Benutzeroberfläche
Outlets deklarieren
Fügen wir dem View-Controller eine Tabellenansicht hinzu, um eine Liste von Aufgaben anzuzeigen. Bevor wir das tun, müssen wir eine Steckdose für die Tabellenansicht erstellen. Eine Steckdose ist nichts anderes als eine Eigenschaft, die im Interface Builder sichtbar ist und eingestellt werden kann. Um eine Steckdose in der ViewController
-Klasse zu deklarieren, setzen wir der Eigenschaft, einer Variablen, das @IBOutlet
-Attribut als Präfix voran.
1 |
class ViewController: UIViewController { |
2 |
@IBOutlet var tableView: UITableView! |
3 |
|
4 |
override func viewDidLoad() { |
5 |
super.viewDidLoad() |
6 |
}
|
7 |
|
8 |
override func didReceiveMemoryWarning() { |
9 |
super.didReceiveMemoryWarning() |
10 |
}
|
11 |
}
|
Beachten Sie, dass der Ausgang implizit optional ausgepackt ist. Ein Was? Lassen Sie mich zu Beginn sagen, dass eine Steckdose immer ein optionaler Typ sein muss. Der Grund ist einfach. Jede Eigenschaft der ViewController
-Klasse muss nach der Initialisierung einen Wert haben. Eine Steckdose ist jedoch erst zur Laufzeit mit der Benutzeroberfläche verbunden, nachdem die ViewController
-Instanz initialisiert wurde, daher der optionale Typ.
Warte eine Minute. Der TableView
-Ausgang wird als implizit nicht ausgepackt, optional und nicht als optional deklariert. Kein Problem. Wir können den TableView
-Ausgang als optional deklarieren, indem wir das Ausrufezeichen im obigen Snippet durch ein Fragezeichen ersetzen. Das würde gut kompilieren. Dies würde jedoch auch bedeuten, dass wir die Eigenschaft jedes Mal explizit entfernen müssen, wenn wir auf den im optionalen Wert gespeicherten Wert zugreifen möchten. Dies wird schnell umständlich und ausführlich.
Stattdessen deklarieren wir den outlet TableView
als implizit unwrapped optional, was bedeutet, dass wir das optionale nicht explizit auspacken müssen, wenn wir auf seinen Wert zugreifen müssen. Kurz gesagt, ein implizit entpacktes optionales Element ist optional, aber wir können auf den im optionalen Element gespeicherten Wert wie eine reguläre Variable zugreifen. Es ist wichtig zu beachten, dass Ihre Anwendung abstürzt, wenn Sie versuchen, auf ihren Wert zuzugreifen, wenn kein Wert festgelegt wurde. Das ist die Frage. Wenn die Steckdose richtig angeschlossen ist, können wir sicherstellen, dass die Steckdose richtig eingestellt ist.
Ausgänge verbinden
Nachdem der Ausgang deklariert wurde, ist es an der Zeit, ihn im Interface Builder zu verbinden. Öffnen Sie Main.storyboard, und wählen Sie den Ansichtscontroller aus. Wählen Sie im Menü Editor die Option Einbetten in > Navigation Controller. Dies setzt den View-Controller als Root-View-Controller eines Navigations-Controllers. Mach dir jetzt keine Sorgen darüber.
Ziehen Sie eine UITableView
-Instanz aus der Objektbibliothek in die Ansicht des Ansichtscontrollers, und fügen Sie die erforderlichen Layouteinschränkungen hinzu. Wenn die Tabellenansicht ausgewählt ist, öffnen Sie den Inspektor Verbindungen und legen Sie die DataSource
- und Delegate
-Ausgänge der Tabellenansicht auf den View-Controller fest.



Wenn der Connections Inspector noch geöffnet ist, wählen Sie den View-Controller aus, und verbinden Sie den TableView
-Ausgang mit der gerade hinzugefügten Tabellenansicht. Dies verbindet den TableView
-Ausgang der ViewController
-Klasse mit der Tabellenansicht.
5. Protokolle
Bevor wir die Anwendung erstellen und ausführen können, müssen wir die Protokolle UITableViewDataSource
und UITableViewDelegate
in der ViewController
-Klasse implementieren. Dies beinhaltet mehrere Dinge.
Schritt 1: Übereinstimmung mit Protokollen
Wir müssen dem Compiler mitteilen, dass die ViewController
-Klasse den UITableViewDataSource
- und UITableViewDelegate
-Protokollen entspricht. Die Syntax sieht ähnlich aus wie in Objective-C.
1 |
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { |
2 |
...
|
3 |
}
|
Die Protokolle, denen die Klasse entspricht, sind nach der Oberklasse UIViewController
im obigen Beispiel aufgelistet. Wenn eine Klasse keine Superklasse hat, was in Swift nicht ungewöhnlich ist, werden die Protokolle unmittelbar nach dem Klassennamen und dem Doppelpunkt aufgelistet.
Schritt 2: Implementieren des UITableViewDataSource
-Protokolls
Da das UITableViewDelegate
-Protokoll keine erforderlichen Methoden definiert, implementieren wir das UITableViewDataSource
-Protokoll nur für den Augenblick. Bevor wir das tun, erstellen wir eine Variableneigenschaft, um die Aufgaben zu speichern, die wir in der Tabellenansicht auflisten werden.
1 |
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { |
2 |
@IBOutlet var tableView: UITableView! |
3 |
|
4 |
var items: [String] = [] |
5 |
|
6 |
...
|
7 |
}
|
Wir deklarieren eine Variableneigen items
des Typs [String]
und setzen ein leeres Array []
als Anfangswert. Dies sollte jetzt bekannt vorkommen. Als Nächstes implementieren wir die zwei erforderlichen Methoden des UITableViewDataSource
-Protokolls.
Die erste erforderliche Methode, numberOfRowsInSection(_:)
, ist einfach. Wir geben einfach die Anzahl der Elemente zurück, die in der Eigenschaft items
gespeichert sind.
1 |
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
2 |
return self.items.count |
3 |
}
|
Die zweite erforderliche Methode, cellForRowAtIndexPath(_:)
, benötigt ein wenig mehr Erläuterungen. Wenn wir die Subscript-Syntax verwenden, fragen wir nach dem richtigen Element aus dem Array von Aufgabenelementen.
1 |
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { |
2 |
// Fetch Item
|
3 |
let item = self.items[indexPath.row] |
4 |
|
5 |
// Dequeue Table View Cell
|
6 |
let tableViewCell = tableView.dequeueReusableCellWithIdentifier("TableViewCell", forIndexPath: indexPath) as! UITableViewCell |
7 |
|
8 |
// Configure Table View Cell
|
9 |
tableViewCell.textLabel?.text = item |
10 |
|
11 |
return tableViewCell |
12 |
}
|
Wir fragen dann in der Tabellenansicht nach einer Zelle mit dem Bezeichner "TableViewCell"
und übergeben den Indexpfad für die aktuelle Zeile. Beachten Sie, dass wir die Zelle in einer Konstanten namens tableViewCell
speichern. Sie müssen tableViewCell
nicht als Variable deklarieren.
Ein weiteres wichtiges Detail ist, dass wir den Rückgabewert von dequeueReusableCellWithIdentifier(_:forIndexPath:)
zu UITableViewCell
zurücknehmen, da es ein Objekt vom Typ AnyObject
zurückgibt, das id
in Objective-C entspricht. Um AnyObject
zu UITableViewCell
downcast, verwenden wir das Schlüsselwort as
.
Wir könnten das as
verwenden? Variante, die auf einen optionalen Typ reduziert wird, aber da wir sicher sein können, dass dequeueReusableCellWithIdentifier(_:forIndexPath:)
immer eine korrekt initialisierte Instanz zurückgibt, können wir das as
! Schlüsselwort, das Ergebnis des Methodenaufrufs wird implizit ausgepackt.
In der nächsten Codezeile konfigurieren wir die Tabellenansichtszelle und setzen den Text des Textlabels mit dem Wert des item
. Beachten Sie, dass in Swift die Eigenschaft textLabel
von UITableViewCell
als optionaler Typ deklariert ist, daher das Fragezeichen. Diese Codezeile könnte gelesen werden, wenn die text
-Eigenschaft des Textlabels auf item
gesetzt wird, wenn textLabel
ungleich nil
ist. Mit anderen Worten, die text
-Eigenschaft des Textlabels
wird nur gesetzt, wenn textLabel nicht nil
ist. Dies ist ein sehr praktisches Sicherheitskonstrukt in Swift, das als optionale Verkettung bekannt ist.
Schritt 3: Wiederverwendung der Zellen
Es gibt zwei Dinge, die wir vor dem Erstellen der Anwendung klären müssen. Zuerst müssen wir der Tabellenansicht mitteilen, dass sie die UITableViewCell
-Klasse verwenden muss, um neue Tabellenansichtszellen zu erstellen. Dazu rufen wir registerClass(_:forCellReuseIdentifier:)
auf, übergeben die UITableViewCell
-Klasse und die bereits verwendete Wiederverwendungs-ID "TableViewCell"
. Aktualisieren Sie die viewDidLoad
-Methode wie unten gezeigt.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
// Register Class for Cell Reuse
|
5 |
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell") |
6 |
}
|
Schritt 4: Hinzufügen von Elementen
Derzeit sind keine Elemente in der Tabellenansicht verfügbar. Dies lässt sich leicht lösen, indem Sie die Eigenschaft items
mit einigen Aufgaben füllen. Es gibt mehrere Möglichkeiten, dies zu erreichen. Ich habe gewählt, um die Eigenschaft items
in der Methode viewDidLoad
zu füllen, wie unten gezeigt.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
// Populate Items
|
5 |
self.items = ["Buy Milk", "Finish Tutorial", "Play Minecraft"] |
6 |
|
7 |
// Register Class for Cell Reuse
|
8 |
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell") |
9 |
}
|
6. Erstellen und Ausführen
Es ist Zeit, unsere Anwendung für eine Drehung zu nehmen. Wählen Sie Ausführen im Xcode-Produktmenü oder drücken Sie Befehl-R. Wenn Sie gefolgt sind und Swift 1.2 verwenden, sollten Sie das folgende Ergebnis erhalten.



Beachten Sie, dass ich oben in der Ansicht in der Navigationsleiste den Titel To Do hinzugefügt habe. Sie können dasselbe tun, indem Sie die title
-Eigenschaft der ViewController
-Instanz in der viewDidLoad
-Methode festlegen.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
// Set Title
|
5 |
self.title = "To Do" |
6 |
|
7 |
// Populate Items
|
8 |
self.items = ["Buy Milk", "Finish Tutorial", "Play Minecraft"] |
9 |
|
10 |
// Register Class for Cell Reuse
|
11 |
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell") |
12 |
}
|
Erfahren Sie mehr in unserem Swift Programming Kurs
Wenn Sie daran interessiert sind, Ihre Swift-Ausbildung auf die nächste Stufe zu bringen, können Sie sich unseren vollständigen Kurs zur Entwicklung von Swift ansehen.
Fazit
Obwohl wir eine einfache Anwendung erstellt haben, haben Sie einige neue Dinge gelernt. Wir haben Vererbungs- und Überschreibungsmethoden untersucht. Wir haben auch Protokolle behandelt und wie das UITableViewDataSource
-Protokoll in der ViewController
-Klasse übernommen wird. Das Wichtigste, was Sie jedoch gelernt haben, ist die Interaktion mit Objective-C-APIs.
Es ist wichtig zu verstehen, dass die APIs des iOS SDK in Objective-C geschrieben sind. Swift wurde entwickelt, um mit diesen APIs kompatibel zu sein. Aufgrund früherer Fehler erkannte Apple, dass Swift in der Lage war, sich in das iOS SDK einzuklinken, ohne jedes einzelne API in Swift neu schreiben zu müssen.
Die Kombination von Objective-C und Swift ist möglich, aber es gibt einige Vorbehalte, die wir im weiteren Verlauf genauer untersuchen werden. Wegen Swifts unablässigem Fokus auf Sicherheit müssen wir von Zeit zu Zeit einige Hürden nehmen. Keine dieser Hürden ist jedoch zu groß, um das zu tun, wie wir im nächsten Artikel erfahren werden, in dem wir weiter an unserer To-Do-Anwendung arbeiten.