Arbeiten mit Table View Controllern in Swift

Geschrieben von Reinder de Vries am 3. August 2020 in App-Entwicklung, iOS

 Arbeiten mit Table View Controllern in Swift

In diesem Tutorial zeige ich Ihnen Schritt für Schritt, wie Table View Controller funktionieren und wie Sie sie verwenden können. Wir gehen auf die gesamte Bandbreite von UITableViewController ein, indem wir uns mit objektorientierter Programmierung, Delegierung und den Mechanismen hinter den Kulissen von Tabellenansichten befassen.

Ein Tabellenansicht-Controller zeigt strukturierte, wiederholbare Informationen in einer vertikalen Liste an. Sie verwenden die Klasse UITableViewController in Ihrer iOS-App, um einen Tabellenansichts-Controller zu erstellen.

Das Arbeiten mit einem Tabellenansichtscontroller bedeutet auch, mit einigen wichtigen iOS-Entwicklungskonzepten zu arbeiten, z. B. mit Unterklassen, dem Delegierungsdesignmuster und der Wiederverwendung von Ansichten.

Es ist wichtig für professionelle und praktische iOS-Entwickler (Sie!), um die Arbeit mit Tabellenansichtscontrollern zu beherrschen. Sobald Sie sich daran gewöhnt haben, an einer so facettenreichen UI-Komponente wie UITableViewController zu arbeiten, werden auch andere komplexere Aspekte der iOS-Entwicklung Sinn machen.

Bereit? Lass uns eintauchen!

  1. Funktionsweise eines Table View Controllers
  2. Einrichten eines einfachen Table View Controllers
  3. Codieren der Datenquelle des Table View Controllers
  4. Bereitstellen von Zellen für den Table View Controller
  5. Reagieren auf Benutzerinteraktion
  6. Weiterführende Literatur

Wie eine View Controller funktioniert

Wenn Sie zuvor eine iOS-App verwendet haben, haben Sie zuvor Table View Controller verwendet. Sie werden so häufig in iOS-Apps verwendet!

Hier ist ein Beispiel für einen Tabellenansicht-Controller:

Beispiel für einen Tabellenansichtscontroller

Ein Tabellenansichtscontroller hat normalerweise diese sichtbaren Komponenten:

  • Eine Tabellenansicht, bei der es sich um die Benutzeroberflächenkomponente oder Ansicht handelt, die auf dem Bildschirm angezeigt wird. Eine Tabellenansicht ist eine Instanz der Klasse UITableView, einer Unterklasse von UIScrollView.
  • Tabellenansichtszellen, bei denen es sich um wiederholbare Zeilen oder Ansichten handelt, die in der Tabellenansicht angezeigt werden. Eine Tabellenansichtszelle ist eine Instanz einer UITableViewCell -Klasse, und diese Klasse wird häufig in Unterklassen unterteilt, um benutzerdefinierte Tabellenansichtszellen zu erstellen.

Ein Table View Controller setzt auch hinter den Kulissen auf die Verwendung dieser Komponenten:

  • Ein Delegat der Tabellenansicht, der für die Verwaltung des Layouts der Tabellenansicht und die Reaktion auf Benutzerinteraktionsereignisse verantwortlich ist. Ein Tabellenansichtsdelegat ist eine Instanz der Klasse UITableViewDelegate.
  • Eine Datenquelle für die Tabellenansicht, die für die Verwaltung der Daten in einer Tabellenansicht verantwortlich ist, einschließlich Tabellenansichtszellen und -abschnitten. Eine Datenquelle ist eine Instanz der Klasse UITableViewDataSource.

Ein Navigationscontroller wird häufig in Verbindung mit einem Tabellenansichtscontroller verwendet, um die Navigation zwischen der Tabellenansicht und nachfolgenden Ansichtscontrollern zu ermöglichen und eine Navigationsleiste über der Tabellenansicht anzuzeigen.

Der interessanteste Teil der Arbeit mit Table View Controllern ist der Table View Controller selbst! Wie denn?

Kennen Sie die Model-View-Controller-Architektur? Gemäß der Model-View-Controller-Architektur sind eine Tabellenansicht und eine Tabellenansichtszelle Ansichten, und ein Tabellenansichtscontroller ist ein Controller.

Ansichten sind dafür verantwortlich, Informationen für den Benutzer sichtbar mit einer Benutzeroberfläche (UI) anzuzeigen. Controller sind verantwortlich für die Implementierung von Logik, die Verwaltung von Daten und die Entscheidungsfindung. Anders gesagt: Sie können keinen Controller sehen, aber er verwaltet das, was Sie durch Ansichten sehen.

Wenn Sie einen Tabellenansichts-Controller in Ihrer App verwenden, unterklassen Sie die UITableViewController -Klasse. Die UITableViewController -Klasse selbst ist eine Unterklasse von UIViewController.

Hier ist die Klassenhierarchie eines Beispieltabellenansicht-Controllers, der Kontaktinformationen anzeigt:

  • eine Instanz von ContactsTableViewController
    • Unterklassen UITableViewController
      • Unterklassen UIViewController

Gemäß den Prinzipien der objektorientierten Programmierung (OOP) erbt die Klasse RaceCar, wenn sie die Klasse Vehicle unterordnet, die Eigenschaften und Funktionen dieser Oberklasse, z. B. maxSpeed und drive().

Die Vehicle -Klasse wird dann als Oberklasse von RaceCar bezeichnet. Dieses Prinzip nennt man Vererbung.

Verwirrend? Es kann sein! Denken Sie so darüber nach: damit Ihr Tabellenansichts-Controller einwandfrei funktioniert, müssen Sie eine Reihe von Code erben, sodass Sie nicht den gesamten Code selbst schreiben müssen. Die Klassenhierarchie und OOP strukturieren diese Vererbung.

Klassendiagramm des Tabellenansichts-Controllers

Sie können mit Tabellenansichten arbeiten, ohne einen Tabellenansichts-Controller zu verwenden. Fügen Sie einfach einen UITableView zu einem View Controller hinzu, stellen Sie ihm Implementierungen des Tabellenansichtsdelegaten und der Datenquellenfunktionen zur Verfügung, und fertig.

Die UITableViewController -Klasse stellt Standardimplementierungen dieser Tabellenansichtsdelegaten- und Tabellenansichtsdatenquellenfunktionen bereit. Das ist ein entscheidender Aspekt der Arbeit mit einem Table View Controller!

Wie Sie in den nächsten Kapiteln dieses Tutorials sehen werden, werden wir diese Funktionen mit unseren eigenen Implementierungen überschreiben. Wir können den Table View Controller auf diese Weise anpassen.

Erfahren Sie, wie Sie iOS-Apps erstellen

Erste Schritte mit iOS 14 und Swift 5

Melden Sie sich für meinen iOS-Entwicklungskurs an und erfahren Sie, wie Sie großartige iOS 14-Apps mit Swift 5 und Xcode 12 erstellen.

Einrichten eines einfachen Table View Controllers

Lassen Sie uns das alles in die Praxis umsetzen. In diesem Abschnitt werden wir einen einfachen Tabellenansichts-Controller erstellen. Sie werden die Funktionen implementieren, die erforderlich sind, damit es funktioniert, und ich werde erklären, wie sie funktionieren. Lass uns weitermachen!

Sie können 3 verschiedene Ansätze zum Arbeiten mit Benutzeroberflächen in Xcode verwenden:

  1. Ansichten programmgesteuert erstellen, dh von Hand codieren
  2. Benutzeroberflächen im Interface Builder einrichten und über XIBs mit Swift-Code verbinden
  3. Benutzeroberflächen und ihre Übergänge im Interface Builder mithilfe von Storyboards einrichten
  4. (Technisch gesehen können Sie auch SwiftUI verwenden, aber für Tabellenansichts-Controller geht dies über den Rahmen dieses Lernprogramms hinaus.)

Es ist mühsam und unproduktiv, Benutzeroberflächen von Hand zu codieren. Wenn Sie mit Storyboards nicht vorsichtig sind, verbergen sie die Komplexität vor dem Entwickler. Wir werden mit Storyboards arbeiten, während wir genau herausfinden, wie sie hinter den Kulissen funktionieren.

Folgendes werden Sie tun:

Erstellen Sie zunächst ein neues Xcode-Projekt über Datei → Neu → Projekt …. Stellen Sie sicher, dass Sie die App-Vorlage auswählen und Storyboard für die Benutzeroberfläche und UIKit App Delegate für den Lebenszyklus auswählen.

Führen Sie dann diese Schritte aus:

  1. Klicken Sie mit der rechten Maustaste auf Ihr Projekt im Projektnavigator und wählen Sie Neue Datei …
  2. Wählen Sie die Cocoa Touch-Klassenvorlage (iOS)
  3. Wählen Sie UITableViewController für die Unterklasse von
  4. Benennen Sie die Klasse ContactsTableViewController
  5. Kreuzen Sie auch keine XIB-Datei erstellen an
  6. Schließlich, klicken Sie auf Weiter und speichern Sie die Datei neben den anderen Swift-Dateien

Zuletzt tun Sie dies:

  1. Gehen Sie in Main.storyboard und entfernen Sie die vorhandene View Controller-Szene
  2. Fügen Sie dem Storyboard über die Bibliothek einen neuen Table View Controller hinzu
  3. Gehen Sie bei ausgewähltem Table View Controller zum Attribute Inspector und aktivieren Sie das Kontrollkästchen Is Initial View Controller
  4. ContactsTableViewController

Das war’s! Sie haben jetzt einen Tabellenansichts-Controller im Storyboard des Projekts und haben ihn mit der Swift-Klasse ContactsTableViewController verbunden.

Xcode Table View Controller einrichten

Wie Sie erraten haben, ist Ihre ContactsTableViewController -Klasse eine Unterklasse von UITableViewController. Sie können das in der Swift-Datei oben in der Klassendeklaration sehen.

class ContactsTableViewController: UITableViewController { ···

Diese Syntax bedeutet: Die Klasse ContactsTableViewController ist eine Unterklasse von UITableViewController.

Wenn Sie mit der rechten Maustaste auf „UITableViewController“ klicken, während Sie die Wahltaste gedrückt halten, können Sie in der Klassendeklaration sehen, dass UITableViewController eine Unterklasse von UIViewController ist und den Protokollen UITableViewDelegate und UITableViewDataSource entspricht.

Siehe Table View Controller-Dokumentation in Xcode

Das ist die Leistung des Table View Controllers! Es gibt uns nicht nur die einzelnen Komponenten, um eine Tabellenansicht zu erstellen, der Controller bietet auch eine Standardimplementierung. Aus diesem Grund unterklassen wir UITableViewController , anstatt einen leeren Ansichts-Controller mit einer Tabellenansicht zu erstellen.

Zu diesem Zeitpunkt können Sie Ihre App mit Command-R oder der Wiedergabetaste ausführen und den leeren Tabellenansicht-Controller auf dem Bildschirm anzeigen.

Warum ist das übrigens so? Wir haben noch nichts codiert! Das liegt daran, dass der Table View Controller eine Standardimplementierung hat, die nur leere Zellen auf dem Bildschirm anzeigt. Ordentlich!

 Beispiel für einen Tabellenansicht-Controller im iPhone Simulator

Abhängig von Ihrer Xcode-Version haben wir den Szenendelegaten verwendet, um den anfänglichen Ansichtscontroller Ihres App-Projekts einzurichten. Erfahren Sie hier mehr: Der Szenendelegierte in Xcode

Eine XIB und eine NIB sind im Grunde dasselbe – sie enthalten Layoutinformationen. Eine XIB hat ein XML-Format, während eine NIB ein Binärformat hat. Aus diesem Grund sprechen die Funktionen von UIKit immer von einer „Feder“, während Xcode sie immer als „xib“ bezeichnet.

Codierung der Datenquelle des Tabellenansicht-Controllers

Nachdem Ihr Tabellenansicht-Controller eingerichtet wurde, erwecken wir ihn zum Leben! In diesem Kapitel konzentrieren wir uns auf die verschiedenen Funktionen, die Sie implementieren müssen, damit Ihr Table View Controller funktioniert.

Wie bereits erläutert, gehören diese Funktionen entweder zum Delegaten des Tabellenansichtscontrollers oder zur Datenquelle des Tabellenansichtscontrollers. Beide Protokolle verwenden die Delegierung, um die Tabellenansicht anzupassen.

Die wichtigsten Funktionen für UITableViewDataSource sind:

  • numberOfSections(in:)
  • tableView(_:numberOfRowsInSection:)
  • tableView(_:cellForRowAt:)

Weitere relevante Funktionen für UITableViewDelegate sind:

  • tableView(_:didSelectRowAt:)
  • tableView(_:willDisplay:for:)

Weitere Funktionen finden Sie in der Apple-Entwicklerdokumentation für UITableViewDelegate und UITableViewDataSource.

Hinzufügen der Kontaktdaten

Sie beginnen mit dem Hinzufügen der Kontaktinformationsdaten für den Table View Controller. Fügen Sie der Klasse ContactsTableViewController die folgende Eigenschaft hinzu:

let contacts:] = , , , , , , , , , , , , , , , , ]

Das ist ein Rolodex, den wir alle gerne hätten, oder? So funktioniert’s:

  • Die let contacts deklariert eine Konstante mit dem Namen contacts. Sie haben es der Klasse als Eigenschaft hinzugefügt, sodass jede Klasseninstanz im gesamten Klassencode Zugriff auf diese Konstante hat.
  • Der Typ von contacts ist ], ein Array von Arrays von Zeichenfolgen. Sie erstellen im Wesentlichen ein Array, dessen Elemente Arrays von Zeichenfolgen sind. (Ein Variablenname und sein Typ werden durch einen Doppelpunkt getrennt :)
  • Der = -Code weist contacts ein Array-Literal zu, das mit den Namen und Telefonnummern einiger Milliardäre gefüllt ist.

Zu einem späteren Zeitpunkt können wir die Anzahl der Elemente im Array mit contacts.count verwenden. Und wir können einzelne Namen und Telefonnummern mit contacts und contacts mithilfe der Indexsyntax abrufen.

Registrieren einer Tabellenansichtszellenklasse

Bevor Sie Zellen in einem Tabellenansichtscontroller verwenden können, müssen Sie sie in der Tabellenansicht registrieren. Sie können dies auf zwei Arten tun:

  1. Durch Bereitstellen einer Tabellenansichtszellenklasse und einer Kennung
  2. Durch Bereitstellen einer Tabellenansichtszelle XIB und einer Kennung

Wenn Sie eine benutzerdefinierte Tabellenansichtszelle verwenden, möchten Sie höchstwahrscheinlich eine XIB dafür registrieren. Wenn Sie die Standardtabellenansichtszellen oder eine andere programmgesteuerte Zelle verwenden, registrieren Sie die Klasse. Wir werden die Klasse benutzen, vorerst!

Fügen Sie der Funktion viewDidLoad() den folgenden Code hinzu:

tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellIdentifier")

Stellen Sie sicher, dass Sie es unterhalb der Zeile super.viewDidLoad() hinzufügen. Wie Sie wahrscheinlich wissen, ist die Funktion viewDidLoad() Teil des Lebenszyklus des View Controllers und gehört zur Klasse UIViewController.

Sie überschreiben die Funktion viewDidLoad(), um auf dieses Ereignis im Lebenszyklus eines View Controllers zu reagieren, sodass Sie Ihre Ansicht nach dem Laden einrichten können. In unserem Fall verwenden wir die Funktion, um die Tabellenansichtszelle zu registrieren.

Wenn Sie eine Tabellenansichtszelle registrieren, müssen Sie auch einen Bezeichner angeben. Dies dient lediglich dazu, die Klasse der Zelle mit einem Namen zu verknüpfen, den Sie später verwenden können, wenn Sie die Zelle in tableView(_:cellForRowAt:) .

Bist du noch bei mir? Lass uns weitermachen!

Implementieren von „numberOfSections(in:)“

Die erste Delegatfunktion, die wir implementieren werden, ist numberOfSections(in:).

Eine Tabellenansicht kann mehrere Abschnitte oder Gruppen haben. Jede Gruppe hat eine Kopfzeile, die über der vertikalen Zellenreihe schwebt. In einer Kontakte-App können Sie Kontakte alphabetisch gruppieren. Dies geschieht tatsächlich in der Kontakte-App auf dem iPhone, in der Kontakte von A bis Z gruppiert sind.

Die App, die wir erstellen, hat nur einen Abschnitt. Fügen Sie der Klasse die folgende Funktion hinzu:

override func numberOfSections(in tableView: UITableView) -> Int{ return 1}

Einfach, oder? Die Funktion gibt beim Aufruf 1 zurück.

Implementieren von „tableView(_:numberOfRowsInSection:)“

Eine ähnliche Funktion ist tableView(_:numberOfRowsInSection:) . Anstatt die Anzahl der Abschnitte anzugeben, wird die Anzahl der Zeilen in einem Abschnitt angegeben. Da in einer Tabellenansicht Zellen in einer vertikalen Liste angezeigt werden, entspricht jede Zelle einer Zeile in der Tabellenansicht.

Die App, die wir erstellen, hat nur einen Abschnitt, und dieser eine Abschnitt enthält eine Anzahl von Elementen, die der Anzahl der Elemente im Array contacts entspricht. Also, das ist contacts.count!

Fügen Sie der Klasse die folgende Funktion hinzu:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{ return contacts.count}

Sehen Sie, wie das funktioniert? Wir geben einfach contacts.count zurück. Wenn Sie contacts einen anderen Namen und eine andere Telefonnummer hinzufügen würden, würde dies auch in der Tabellenansicht gut angezeigt.

Zeilen und Abschnitte verstehen

Unsere Kontakt-App ist eindimensional, zeigt nur eine Liste mit Namen und Telefonnummern an und verwendet keine Gruppen. Aber was ist, wenn Sie eine gruppierte Tabellenansicht haben?

In den meisten Fällen wäre Ihre Datenquelle wie das Array contacts auch mehrdimensional. Sie würden Gruppen auf der ersten Ebene und einzelne Elemente auf der zweiten Ebene „unter“ den Gruppen organisieren.

- Countries - A - Afghanistan - Albania - ... - B - Bahamas - Bahrain - ... - C - Cambodia - Cameroon - ...

Die Anzahl der Gruppen ist gleich countries.count und die Anzahl der Länder in einer einzelnen Gruppe ist gleich countries.count, wobei x der Abschnittsindex ist. Dieser Abschnittsindex wird als Parameter in tableView(_:numberOfRowsInSection:) bereitgestellt.

Haben Sie bemerkt, dass diese beiden Funktionen einen Parameter namens tableView haben? Das ist Teil des objektorientierten Programmierprinzips. Sie können technisch eine Datenquelle und einen Delegaten für Tabellenansichten verwenden, um mehrere Tabellenansichten anzupassen. Sie würden tableView verwenden, um festzustellen, mit welcher Tabellenansicht Sie arbeiten.

Stellen Sie sich vor, Sie haben eine Kontakt-App, die Telefonnummern nach Namen oder Telefonnummern nach Unternehmen anzeigen kann. Sie können dies auf verschiedene Arten implementieren, indem Sie beispielsweise Ihre Tabellenansichts-Controller wiederverwenden. Oder was ist, wenn Sie das Layout Ihrer Kontakte-App wiederverwenden möchten, um ähnliche Informationen wie Restaurants, Veranstaltungsorte oder Skype-Benutzernamen anzuzeigen? Hier kommt die Wiederverwendung von Code mit OOP ins Spiel!

Zellen für den Table View Controller bereitstellen

Wir kommen dorthin! Kommen wir zur wichtigsten Funktion eines Table View Controllers: tableView(_:cellForRowAt:).

Wir werden die Funktion implementieren, bevor wir in die Details eintauchen, aber es gibt ein paar Dinge, die Sie darüber verstehen müssen:

  • Wenn es heißt
  • Was ist ein Indexpfad?
  • Wie werden Zellen wiederverwendet?

Fügen Sie der Klasse ContactsTableViewControllerzunächst die folgende Funktion hinzu:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{}

So funktioniert’s:

  • Die Funktion überschreibt ihre Superklassenimplementierung von UITableViewController . Inzwischen weißt du, wie das funktioniert, oder? Wir überschreiben die Standardimplementierung und ersetzen unsere eigene. Das liegt daran, dass UITableViewController den Delegaten und die Datenquelle der Tabellenansicht bereits für uns implementiert hat.
  • Wie zuvor hat die Funktion einen Parameter tableView , mit dem wir die Tabellenansicht identifizieren können, in der diese Funktion aufgerufen wird.
  • Ein weiterer Parameter ist indexPath mit dem Argument label cellForRowAt . Der Indexpfad identifiziert die Indizes row und section der Zelle. Dazu später mehr.
  • Der Rückgabetyp der Funktion ist UITableViewCell. Hey, das ist interessant. Diese Funktion wird vom Table View Controller jedes Mal aufgerufen, wenn wir eine Tabellenansichtszelle bereitstellen müssen!

Wenn Sie in dieser App durch die Kontakte scrollen, wird jedes Mal, wenn eine Zelle auf dem Bildschirm angezeigt werden muss, die Funktion tableView(_:cellForRowAt:) aufgerufen. Jedes Mal! Ich werde es dir gleich beweisen.

Als nächstes schreiben wir den Funktionskörper. Fügen Sie den folgenden Code in die Funktion ein:

let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath)print("\(#function) --- section = \(indexPath.section), row = \(indexPath.row)")cell.textLabel?.text = contactsreturn cell

Hier ist, was passiert:

  • Zuerst entfernen wir eine Zelle mit einer Kennung. Es ist genau die gleiche Kennung, die wir zuvor bei der Registrierung der Zelle verwendet haben. Auf diese Weise weiß die Tabellenansicht, welche Art von Zelle wir wollen. Die Zelle aus der Warteschlange wird der Konstante cell zugewiesen. Jetzt haben wir eine Tabellenansichtszelle zum Arbeiten. Mehr zum Dequeuing später.
  • Dann drucken wir einige Informationen auf der Konsole aus. Auf diese Weise können wir sehen, wann diese Funktion aufgerufen wird, wenn die App ausgeführt wird.
  • Dann weisen wir der Textbeschriftung dieser Tabellenansichtszelle den Namen des Kontakts zu. Das contacts enthält den Wert des Namens des Kontakts, zu dem wir mit indexPath.row gelangen. Jede Instanz von UITableViewCell hat eine Eigenschaft textLabel von UILabel, und jedes Label dieses Typs hat eine Eigenschaft text. Sie verwenden es, um den Text auf dem Etikett festzulegen.

Keine Sorge, wir werden jedes dieser Dinge genauer durchgehen. Prüfen Sie zunächst, ob Sie Ihre App ausführen können. Sehen Sie Kontaktnamen? Sehen Sie die Debug-Ausgabe in der Konsole? Versuchen Sie, die App zu scrollen!

Table view controller rows

Wann wird „tableView(_:cellForRowAt:)“ aufgerufen?

Wenn Sie die Kontakte-App ausgeführt und mit dem Scrollen nach oben und unten herumgespielt haben, müssen Sie feststellen, dass bei jedem Scrollen die Debug-Ausgabe in der Konsole angezeigt wird.

Jedes Mal, wenn eine Zelle angezeigt wird, die zuvor nicht auf dem Bildschirm angezeigt wurde, wird die Funktion tableView(_:cellForRowAt:) aufgerufen und eine neue Zeile in der Konsole angezeigt.

Wann wird also tableView(_:cellForRowAt:) aufgerufen? Jedes Mal, wenn eine Tabellenansicht Zelle muss auf dem Bildschirm angezeigt werden!

Der Tabellenansicht-Controller hat festgestellt, dass eine Zelle benötigt wird, und ruft daher tableView(_:cellForRowAt:) auf. Unsere Implementierung dieser Funktion hebt die Warteschlange einer Zelle auf, ändert sie und stellt sie dem Tabellenansichts-Controller zur Verfügung. Der Tabellenansichts-Controller und das UIKit-Framework rendern ihn dann grafisch auf dem Bildschirm.

Was ist ein Indexpfad?

Jedes Mal, wenn der Tabellenansichts-Controller eine Zelle von tableView(_:cellForRowAt:) benötigt, stellt er einen Indexpfad als Argument für die Funktion bereit. Innerhalb des Funktionskörpers können Sie mit dem Parameter indexPath genau wissen, welche Zelle der Tabellenansicht-Controller benötigt.

Ein Indexpfad ist wie eine Adresse oder eine Koordinate in einem Raster. Ein typisches Diagramm hat eine X-Achse und eine Y-Achse, sodass Sie eine Koordinate in diesem Diagramm als x, y wie 0, 1 und 42, 3 ausdrücken können. In ähnlicher Weise enthält eine Tabelle Zeilen und Spalten mit Indizes.

Eine Tabellenansicht verwendet Abschnitte und Zeilen. Wie bereits erwähnt, können Sie Abschnitte verwenden, um Zellen zu gruppieren. Unsere App hat nur einen Abschnitt und contacts.count Zeilen. Die Zeilen der Tabellenansicht verlaufen von oben nach unten.

Anders gesagt: Die Abschnitte und Zeilen einer Tabellenansicht sind die Spalten und Zeilen einer Tabelle. Ein Indexpfad definiert eine Position in der Tabellenansicht mithilfe einer Zeile und eines Abschnitts.

Die Zeilen und Abschnitte werden durch Zahlen dargestellt, die als Indizes bezeichnet werden. Diese Indizes beginnen bei Null, sodass die erste Zeile und der erste Abschnitt die Indexnummer 0 haben.

Wenn Sie auf den vorherigen Screenshot zurückblicken, ist dies viel sinnvoller. Die erste Zelle hat den Indexpfad 0, 0, die zweite Zelle 0, 1 und setzt sich bis zur letzten sichtbaren Zelle mit dem Indexpfad 0, 11 fort.

Der Mechanismus zur Wiederverwendung von Tabellenansichten

Was an der Tabellenansicht am bemerkenswertesten ist, ist der Mechanismus zur Wiederverwendung von Zellen. Es ist eigentlich ganz einfach.

  • Jedes Mal, wenn ein Tabellenansicht-Controller eine Zelle auf dem Bildschirm anzeigen muss, wird die Funktion tableView(_:cellForRowAt:) aufgerufen, wie wir zuvor besprochen haben.
  • Anstatt bei jedem Aufruf dieser Funktion eine neue Tabellenansichtszelle zu erstellen, wird eine zuvor erstellte Zelle aus einer Warteschlange ausgewählt.
  • Die Zelle wird in einen leeren Zustand zurückgesetzt, ihr Erscheinungsbild wird gelöscht, und die Zelle wird in tableView(_:cellForRowAt:) erneut angepasst.
  • Wenn eine Zelle außerhalb des Bildschirms gescrollt wird, wird sie nicht zerstört. Es wird der Warteschlange hinzugefügt und wartet darauf, wiederverwendet zu werden.

Es ist ziemlich clever, oder? Anstatt Zellen zu erstellen und zu löschen, verwenden Sie sie einfach wieder. Aber … warum?

Es ist viel weniger speicherintensiv, Zellen wiederzuverwenden. Der Tabellenansicht-Controller würde beim Erstellen und Löschen von Zellen ständig in den Speicher schreiben. Die Verwaltung des Speichers wäre auch intensiver. Wenn Zellen wiederverwendet werden, wird der Speicher effizienter verwendet und es sind weniger Speicheroperationen erforderlich.

Außerdem ist es etwas weniger CPU-intensiv, Zellen wiederzuverwenden, anstatt sie zu erstellen und zu löschen, da bei der Wiederverwendung im Vergleich zum Erstellen und Löschen von Zellen einfach weniger Vorgänge erforderlich sind.

Wenn Sie schnell durch eine Tabellenansicht scrollen, werden keine neuen Zellen angezeigt – Sie sehen immer wieder dieselben Zellen mit neuen Informationen.

Der Code für die Wiederverwendung von Zellen lautet wie folgt:

let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath)

Die Funktion dequeueReusableCell(withIdentifier:) versucht, eine Zelle aus der Warteschlange zu entfernen. Wenn sich keine Zellen in der Warteschlange befinden, wird eine Zelle für uns erstellt. Der Bezeichner wird verwendet, um jeden Zellentyp in einer eigenen Warteschlange zu halten und um sicherzustellen, dass die richtige Klasse zum Erstellen neuer Zellen verwendet wird.

Erfahren Sie, wie Sie iOS-Apps erstellen

Erste Schritte mit iOS 14 und Swift 5

Melden Sie sich für meinen iOS-Entwicklungskurs an und erfahren Sie, wie Sie großartige iOS 14-Apps mit Swift 5 und Xcode 12 erstellen.

Reaktion auf Benutzerinteraktion

In unserem Table View Controller fehlt eines: Die Möglichkeit, Personen in unserer Kontaktliste anzurufen! Aber bevor wir das tun, stellen wir sicher, dass Sie auch die Telefonnummer eines Kontakts im Table View Controller sehen können.

Die Standardklasse UITableViewCell hat 4 verschiedene Typen, wie in der Aufzählung UITableViewCellStyle ausgedrückt. Sie können wählen zwischen:

  • .default – eine einfache Ansicht mit einer Zeile schwarzem Text
  • .value1 – eine einfache Ansicht mit einer schwarzen Textzeile links und einer kleinen blauen Beschriftung rechts (wird in der App Einstellungen verwendet)
  • .value2 – eine einfache Ansicht mit einer schwarzen Textzeile rechts und einer kleinen blauen Beschriftung links (wird in der Kontakte-App verwendet)
  • .subtitle – eine einfache Ansicht mit einer Zeile schwarzen Textes und einer kleineren Zeile grauen Textes darunter

Die meisten Entwickler verwenden heutzutage benutzerdefinierte Tabellenansichtszellen, sodass diese Zelltypen nicht so häufig angezeigt werden. Aber sie sind da!

Wir müssen den Code in tableView(_:cellForRowAt:)leicht anpassen. Ersetzen Sie die erste Zeile der Funktion durch den folgenden Code:

var cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")if cell == nil{ cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellIdentifier")}

Wenn Sie genau hinschauen, werden Sie feststellen, dass wir den for: indexPath -Teil des dequeueReusableCell(...) -Aufrufs entfernt haben. Stattdessen gibt diese Funktion jetzt ein optionales zurück. Wenn eine Zelle nicht aus der Warteschlange entfernt werden kann, gibt die Funktion nil zurück.

Wir springen dann in uns selbst, um die Zelle zu erstellen, wenn es nil ist. Sie sehen das im zweiten Teil des Codes. Sie verwenden eine bedingte Anweisung if, um zu überprüfen, ob cell gleich nil ist, und wenn dies zutrifft, erstellen Sie die Zelle mit dem Initialisierer UITableViewCell(style:reuseIdentifier:) .

Dieser Initialisierer erhält zwei Argumente, den Zellstil .subtitle und den zuvor verwendeten Bezeichner.

An dieser Stelle haben wir ein Problem, denn cell ist jetzt optional! Sein Typ ist UITableViewCell? , aber der Funktionsrückgabetyp erfordert, dass wir eine Instanz mit dem nicht optionalen Typ UITableViewCell .

Glücklicherweise ist dies einer der Fälle, in denen wir force unwrapping sicher verwenden können, um den optionalen Wert zu entpacken. Aufgrund der Art und Weise, wie unser Code geschrieben ist, ist es unmöglich, dass cell nil über die Bedingung hinausgeht. Sie können garantieren, dass cell nicht nil nach der if -Anweisung ist.

Stellen Sie sicher, dass Sie die Funktion aktualisieren, um force unwrapping für cell zu verwenden. Fügen Sie außerdem die folgende Codezeile unter cell!.textLabel ... hinzu, um den Untertitel der Zelle und die Telefonnummer des Kontakts festzulegen.

cell!.detailTextLabel?.text = contacts

Die gesamte Funktion sieht jetzt so aus:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{ var cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier") if cell == nil { cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellIdentifier") } print("\(#function) --- section = \(indexPath.section), row = \(indexPath.row)") cell!.textLabel?.text = contacts cell!.detailTextLabel?.text = contacts return cell!}

Stellen Sie schließlich sicher, dass Sie die folgende Zeile aus viewDidLoad() entfernen. Dadurch wird verhindert, dass die Tabellenansicht Zellen mit dem falschen Typ initialisiert.

tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellIdentifier")

Mächtig gut! Führen Sie Ihre App mit Befehl + R oder der Wiedergabetaste aus und prüfen Sie, ob sie funktioniert. Sehen Sie Namen und Telefonnummern? Gut!

Fügen wir dann für die Pièce-de-résistance diese Benutzerinteraktionsfunktion hinzu. Nachdem Sie nun die Feinheiten des Table View Controllers kennengelernt haben, wissen Sie wahrscheinlich bereits, wie diese nächste Funktion funktioniert.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){ if let url = URL(string: "tel://" + contacts) { UIApplication.shared.open(url) }}

Auch hier überschreiben wir die Standardimplementierung der Funktion tableView(_:didSelectRowAt:). Diese Funktion wird aufgerufen, wenn ein Benutzer auf die Zelle einer Tabellenansicht tippt, und gehört zum Protokoll UITableViewDelegate. Wie bei den anderen Funktionen wird der Indexpfad der angezapften Zelle bereitgestellt.

Im Funktionskörper erstellen wir einfach eine tel:// URL aus der Telefonnummer. Wir weisen die App dann an, diese URL zu öffnen, wodurch iOS effektiv angewiesen wird, einen Anruf an diese Nummer zu initiieren. Dieser Code dient nur zur Veranschaulichung. Beachten Sie, dass dies nicht auf dem iPhone Simulator funktioniert, und dass die Zahlen in unserer App sind gefälscht. (Trotzdem würden Sie diesen Code verwenden, wenn Sie eine echte Kontakt-App erstellen!)

Sie können der Funktion den folgenden Code hinzufügen, wenn Sie überprüfen möchten, ob sie ordnungsgemäß funktioniert.

print("\(#function) --- Calling: \(contacts)")

Dies gibt eine Debug-Meldung aus, wenn Sie auf die Zelle der Tabellenansicht tippen.

Weiterführende Literatur

Und das ist alles! Es war eine ziemliche Reise, aber jetzt wissen Sie, wie ein Tabellenansichts-Controller funktioniert.