Lekce 5 - Seznámení se s důležitou komponentou TableView
V minulé lekci, Jednoduchá kalkulačka pro iOS ve Swift, jsme si upevnili znalosti Autolayout a
StackView
na jednoduchých ukázkách použití.
StackView
skládalo komponenty pod sebe nebo jsme si u něj mohli
nastavit i vodorovný směr.
V dnešním Swift tutoriálu se budeme věnovat TableView
. Je
totiž základem spousty aplikací a pravděpodobně se mu nevyhnete. Ať už si
otevřete aplikaci pro zprávy, volání, poznámky a hromadu dalších,
díváte se v první řadě na TableView
.
TableView
je ideální způsob, jak uživateli prezentovat
spoustu dat nebo chcete-li jejich kolekci. Může to být seznam úkolů,
kontaktů, hudebních alb apod. Právě v takových případech je
TableView
jasná volba. Umožní vám snadno zobrazit prakticky
neomezeně prvků či objektů pěkně pod sebou a vyřešit scrollování či
jejich výběr. Velmi snadno také provedete mazání.
Vytvoření projektu
Teorie by mohla pro úvod stačit a pojďme si rovnou ukázat jednoduchý
způsob, jak s TableView
začít. Připravte si buď zbrusu nový
Xcode projekt s iOS aplikací (Single View App) nebo použijte ten z minulých
lekcí.
Nyní si najděte TableView
(ne Table View Controller, k němu
se ještě dostaneme) v knihovně objektů a přetáhněte ho na váš
controller umístěný v Main.storyboard
. Ideálně nastavte
constraints, které mohou být v tomto případě 0
od všech
čtyř stran.

TableView
tedy máme, jak v něm zobrazit data? Na to bude
potřeba zavítat do kódu a to konkrétně do souboru
ViewController.swift
. Nejdříve musíme určit, že tento
controller slouží jako zdroj dat a delegát pro TableView
.
V našem controlleru implementujeme následující dva protokoly:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
To ovšem nestačí. Nyní musíme přidanému TableView
nastavit, že náš controller je jeho DataSource
a také
Delegate
. To abychom mohli vybrat zdroj dat a také reagovat na
události, jako je výběr řádku (respektive buňky Cell
v řeči
iOS). Můžeme si dokonce vybrat, jak toto provést.
Propojení TableView s controllerem pomocí myši
První možností je DataSource
a Delegate
nastavit
přímo v UI designeru, kdy váš označený TableView
přetáhnete
za držení Ctrl nebo pravým klikem na ViewController
a
vyberete dataSource
. To samé platí pro delegate
.
Nebo kliknete pravým na TableView
v seznamu komponent a
přetáhnete dataSource
a delegate
odtud.

Tímto jsme vlastně TableView
řekli, že náš controller bude
reagovat na události TableView
a zároveň poskytovat data. Díky
tomu nás tak TableView
komponenta může upozornit, že uživatel
zvolil nějakou položku či provedl další akci.
Propojení TableView s controllerem pomocí kódu
Druhou možností je DataSource
a Delegate
nastavit
v kódu v metodě viewDidLoad()
. Osobně preferuji tento způsob,
propojení mám na očích a vždy tak vím, že jsem na to nezapomněl.
Kdykoliv vám u TableView
nebude něco fungovat, zkontrolujte
nejdříve, že jej máte správně propojené.
Nejdříve musíte vašemu TableView
vytvořit
Outlet
, což jsme probrali již v minulých lekcích. Poté pouze
nastavíte vaši třídu (tedy controller) jako dataSource
a
delegate
.
override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self tableView.delegate = self }
Nezbytné přípravy máme za sebou a nyní konečně v
TableView
něco zobrazíme.
Implementace protokolů
Aktuálně vám nebude fungovat Build, protože jsme určili, že naše
třída bude implementovat dané protokoly, ale žádný kód jsme do ní zatím
nepřidali. Nyní se očekává, že naše třída zvládne
TableView
poskytnout data. Pro začátek stačí přidat dvě
metody:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { }
První metoda vrací kolik budeme mít řádků. Zatím si práci nebudeme komplikovat více sekcemi. Druhá má poté na starost vrátit danou buňku podle indexu řádky (respektive buňky).
Protože je začátek funkcí stejný, v případě první
začněte psát numberOfRowsIn..
a Xcode vám samo doplní
správnou metodu. Právě k tomuto slouží dvojí názvy parametrů. Ten první
slouží pro identifikaci metody "zvenčí" a druhý používáte v těle
metody. Stejně tak si nechte doplnit druhou metodu tak, že začnete psát
cellForRow..
.
Příprava dat
Vytvoříme si jednoduché pole, které bude reprezentovat položky to-do listu. Bývá zvykem proměnné a konstanty deklarovat hned pod názvem třídy.
var todos = ["Buy coffee", "Take out the trash", "Netflix and chill"]
Je úplně jedno, co sem napíšete. Cílem je mít jakékoliv pole s
nějakými hodnotami, které si v našem TableView
zobrazíme.
Nyní upravíme naše dvě metody.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return todos.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() cell.textLabel?.text = todos[indexPath.row] return cell }
Nic složitého. První metoda vrací počet prvků a druhá vytváří buňku a nastavuje jí text podle jejího indexu. Texty se tahají z našeho pole. Aplikaci můžete spustit a uvidíte všechny prvky vašeho pole pod sebou.

TableView pod pokličkou
Zároveň si zapamatujte, že takto by metoda s cellForRowAt
neměla nikdy vypadat. Chtěl jsem pouze co nejrychleji ukázat funkční
TableView
. Nyní si povíme jak TableView
funguje "pod
pokličkou".
Scrollování položkami bude velmi plynulé i v případě stovky buněk.
TableView
je totiž chytrá komponenta a drží pouze buňky,
které je nutné zobrazit na displeji. Jednoduše můžeme říci, že buňky
recykluje, k čemuž je potřeba důležitá metoda:
let recycledCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
Tyto metody jsou ve skutečnosti dvě, druhá nemá druhý parametr a nikdy byste ji neměli používat. Jedná se o starou verzi, která zůstala zachována kvůli zpětné kompatibilitě.
Možná jste zbystřili parametr withIdentifier
, který musíme
nastavit, aby vše fungovalo. Před použitím ale musíme ještě připravit
TableView
. Otevřete designer a po vybrání komponenty nastavte v
Attributes inspector
hodnotu pro Prototype Cells
na
1
.

Následně je třeba vybrat tuto prototyp buňku, která se objeví v náhledu. Kliknout můžete přímo v náhledu nebo ji vybrat v seznamu komponent tohoto controlleru nalevo.

Poté stačí opět v Attributes inspector
nastavit
Identifier
na "cell", který máme již v kódu výše. Můžete si
samozřejmě zvolit cokoliv jiného.

Nyní stačí aplikaci spustit a měli byste opět vidět své to-do.
A proč celá ta práce? Tímto jsme správně použili
TableView
a naše komponenta je připravená klidně na
zobrazování stovek řádků. Ve skutečnosti totiž vždy existují pouze ty,
které jsou vidět na displeji, takže velký počet dat nezpomalí aplikaci. V
tomto případě nás to určitě trápit nemusí, ale ukázali jsme si
korektní řešení.
Je tu také druhý benefit. Jsme na dobré cestě k vytvoření
komplexnější buňky, respektive řádku TableView
. Právě to
nám naše Prototype Cell
umožňuje. Jednoduše bychom na ní
přetáhli požadované komponenty a vytvořili speciální třídu. To si
ukážeme v pozdějších lekcích kurzu.
Výběr a mazání položek
Nyní si ukážeme, jak v TableView
vybírat a také mazat
jednotlivé položky.
Vybrání položky
Pro situaci, kdy uživatel vybere položku, tu máme připravenou metodu:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { }
Opět můžete začít psát pouze didSelect...
a Xcode vám
metodu nabídne a samo vytvoří. Zatím nebudeme po vybrání řádku dělat
nic extra, pouze si vypíšeme (do Output okna v Xcode) zvolený text, v našem
případě tedy zvolené to-do.
Metodu doplníme o volání funkce print()
. Z parametru
indexPath
si přes vlastnost row
zjistíme, jaký
řádek byl vlastně vybrán, a ten použijeme jako index pro naše
todos[]
pole.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(todos[indexPath.row])
}
Po výběru řádku se vám nyní vypíše vybrané to-do do konzole. Ještě
vyřešíme mazání a tím zatím seznámení s TableView
ukončíme.
Odstranění položky
Na mazání položek tu opět máme připravenou metodu:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { }
Přidáním této metody vlastně dáváme najevo, že chceme
TableView
modifikovat. Nyní jsme již ve stavu, kdy se vám při
swipe gestu doleva zobrazí tlačítko Delete u jednotlivých řádků. Zatím
ale nebude fungovat.
Pomocí parametru editingStyle
si v těle metody zjistíme,
jestli jde o mazání a pokud ano, tak tento prvek smažeme. Nejdříve z
našeho zdrojového pole todos[]
a následně ještě z
TableView
, kde si můžeme vybrat animaci. Kód poté vypadá
následovně.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { todos.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) } }
Animaci si můžete vybrat podle sebe, často se pro zjednodušení a
konzistenci používá volba .automatic
.
TableView staticky
Na závěr úvodního seznámení si ukážeme, jak vytvořit
TableView
, když přesně víme, co budeme mít za buňky,
případně rovnou sekce. Jedná se tedy např. o nějaké menu a jinou situaci,
než když položky čteme z nějaké kolekce. Tento způsob se skvěle hodí,
pokud chceme nabídnout v aplikaci komplexnější možnosti nastavení, abychom
se přiblížili systému iOS, kde je v nastavení zařízení
TableView
na každém kroku.
Přetáhněte si do projektu Table View Controller
, který je
třeba pro statický TableView
. Poté stačí
TableView
označit a v Attributes inspektoru nastavit
Content
na Static Cells
. Následně si můžete vybrat
počet sekcí. Každá může mít vlastní nadpis a také patičku. Jednotlivé
sekce poté stačí označit a vše potřebné nastavit, včetně jednotlivých
řádků.

Jednotlivé buňky nemusí být pouze řádek textu. Pro každou můžete
zvlášť nastavit Style
a mít např. text + podtitulek, přidat
obrázek a tak podobně. Často se nastavuje také vlastnost
Accessory
(opět v Attributes inspektor), která v levé části
buňky zobrazí např. šipku a tím uživateli naznačí, že volba vede na
další obrazovku a tak podobně.

Pro náš nový controller potřebujeme ještě třídu. Přidáme soubor,
ale v dialogu místo Swift file vybereme Cocoa Touch Class
. V
novém dialogu nastavíme, že jde o subclass UITableViewController
a pojmenujeme ho např. SettingsTableViewController
. Teď již jen
stačí v Main.storyboard
vybrat náš nový
TableViewController
a v Identity inspector
mu nastavit
Class
na nově vytvořenou třídu
SettingsTableViewController
.

Statický TableView
zakončíme reakcí na výběr buňky. V
SettingsTableViewController
nemusíme řešit
DataSource
ani Delegate
, protože to vše řeší
třída UITableViewController
. Stačí nám tak pouze implementovat
didSelectRowAt
metodu. Pouze před ní potřebujeme
override
, abychom mohli poskytnout vlastní implementaci.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("Selected row \(indexPath.row) in section \(indexPath.section)") }
Opět bude stačit výpis pro kontrolu. Ve skutečné implementaci by se
celkem hodil switch
(jedno z mála míst, kde má smysl),
zvlášť, pokud byste měli mnoho sekcí a řádků. V této metodě by
například mohl switch
řešit pouze sekce a jednotlivé sekce by
měly vlastní metody ve stylu handleFirstSection(row: Int)
a tak
dále. Mohl by vypadat např. takto:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("Selected row \(indexPath.row) in section \(indexPath.section)") switch indexPath.section { case 0: handleFirstSection(rowIndex: indexPath.row) case 1: handleSecondSection(rowIndex: indexPath.row) default: break } } func handleFirstSection(rowIndex: Int) { switch rowIndex { case 0: // Show account detail break case 1: // Navigate to settings break default: break } } func handleSecondSection(rowIndex: Int) { }
Tímto naše úvodní seznámení s TableView
komponentou
končí. V následujících lekcích tyto znalosti využijeme pro vytvoření
skutečné TODO aplikace včetně databáze.
Příště, v lekci Neobjevujte kolo, použijte CocoaPods, se naučíme používat CocoaPods, který nám umožní využívat balíčkovací systém a snadnou instalaci různých knihoven.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 60x (82.88 kB)
Aplikace je včetně zdrojových kódů v jazyce Swift