Lekce 5 - Upomínač narozenin pro macOS - Dokončení UI a propojení
V minulé lekci, Upomínač narozenin pro macOS - Příprava UI, jsme si vytvořili hlavní okno aplikace a propojili UI komponenty s kódem.
Naše tvorba uživatelského rozhraní ještě neskončila, protože ještě potřebujeme druhé okno, kde bude uživatel moci osoby přidávat. Okno bude jednodušší, protože nám bude stačit pouze zadat jméno a pomocí specializované komponenty vybrat datum narození.
Okno pro přidání osoby
Nejdříve si tedy vytvoříme druhé okno.
Vytvoření modálního okna
Otevřeme Main.storyboard
a z knihovny komponent přetáhneme
Window Controller
. Tímto vlastně získáme to samé, co jsme
měli při vytvoření projektu, tedy okno a na něj navázaný View
Controller.
Měli bychom získat zhruba něco takového:

Pro větší přehlednost upravíme oknům titulky. Prvnímu oknu nastavíme
titulek "BirthdayMinder" podle názvu projektu, tuto vlastnost
Title
naleznete v Attributes inspektoru.

U druhého okna musíme upravit Title
, ovšem u View
Controlleru, protože budeme druhé okno zobrazovat modálním stylem, tedy jako
dialog. Zde jako Title
nastavíme "Přidat osobu".
Otevření modálního okna
Ještě, než na druhé okno nasázíme komponenty, připravíme si navigaci, abychom se na něj vůbec mohli dostat. Možností je více, my se spolehneme na jednoduché segue, podobně jako u iOS aplikací.
Segue vytvoříme v Main.storyboard
pomocí tažení za držení
Ctrl z View Controlleru prvního okna (toho hlavního) na View
Controller druhého okna. Doporučuji tažení provádět v Document outline
(tedy v seznamu komponent v levé části), abyste měli jistotu, že skutečně
táhnete z View Controller.
Z nabídky vybereme možnost "Modal", která způsobí, že nové okno bude dočasně hlavním oknem aplikace a na to první nepůjde do zavření nového okna kliknout. Tím dáme uživateli najevo, že musí dokončit práci s druhým oknem, aby mohl pokračovat. Postup pro vytvoření segue si ukažme na animaci:

Pokud byste chtěli mít jednoduše jen více oken a uživatele neomezovat, stačilo by zvolit možnost "Show".
Nyní potřebujeme vytvořené segue pojmenovat, protože s ním budeme
pracovat v kódu. Nové segue tedy označíme a v Attributes inspektoru mu
nastavíme vlastnost Identifier
na "AddPersonSegue".

Segue by bylo možné vytvořit tažením přímo z tlačítka "Přidat" na druhý View Controller, čímž by toto tlačítko automaticky nové okno otevřelo. Manuální spuštění segue je ale flexibilnější, proto ho použijeme.
Dostáváme se do finále a konečně si okno otevřeme. Postačí k tomu
jeden řádek kódu. Otevřeme si ViewController.swift
a do metody
addBtn_Clicked()
, která slouží k obsluze tlačítka Přidat,
doplníme následující kód:
performSegue(withIdentifier: "AddPersonSegue", sender: self)
Aplikaci můžeme spustit a vyzkoušet:

Bohužel okno se nezobrazuje relativně vzhledem k tomu hlavnímu. To není
něco, co by se dalo v Main.storyboard
snadno naklikat, řešení
pomocí kódu si ukážeme později.
Tvorba UI modálního okna
Prázdné okno je nám celkem k ničemu, proto jej naplníme komponentami.
Budeme potřebovat textové pole pro zadání jména osoby, speciální
komponentu Date Picker
pro výběr data narození a tlačítko pro
potvrzení.
Zavírání okna řešit nebudeme, k tomu má uživatel systémové
tlačítko. Jako bonus přidáme dvojici Label
komponent, aby
uživatel věděl, co má zadat a vybrat. A konečně ještě přidáme
obrázek, který se bude měnit podle nastaveného barevného tématu macOS,
která systém podporuje od verze Mojave.
Push Button
Trochu netradičně začneme odspodu a přidáme Push Button
.
Pomocí AutoLayout mu nastavíme horizontální zarovnání a třeba
15
bodů od spodního okraje okna. Ještě nastavíme fixní
šířku na 80
a nastavíme text "Přidat". Celkem tak máme tři
constraints.
Hodí se říci, že zrovna tato tvorba UI je spíše
doporučení. Jestli chcete experimentovat a máte lepší nápad, jak
komponenty poskládat, tak určitě směle do toho
Date Picker
Nad tlačítko přetáhneme z knihovny komponentu Date Picker
.
Opět ji zarovnáme horizontálně na střed a nastavíme spodní constraint na
15
bodů, tedy na 15
bodů od dříve přidaného
tlačítka. Výchozí nastavení komponenty nám vyhovuje a nic není třeba
měnit.
Text Field
Poslední z nutných komponent je Text Field
, který bude
sloužit k vyplnění jména osoby. Opět mu nastavíme zarovnání na
horizontální střed, 15
bodů od spodní hrany a fixní šířku
90
.
Aktuální UI vypadá zhruba takto:

Labely
Přidáme Label
komponenty, abychom uživateli připomněli, co
má zadat. Komponentám nastavíme velikost fontu System Small a zarovnáme
vertikálně s komponentou, ke které patří.

Potom již bude stačit nastavit jen odsazení z pravé strany. Já jsem
zvolil hodnotu 3
. To samé provedeme pro Label
patřící k Date Picker.
Obrázky pro světlé i tmavé téma macOS
Jak asi víte, macOS od verze Mojave nabízí možnost zapnout tmavé téma. A pokud si nenastavíme vlastní barvy komponent, bude aplikace vypadat dobře v obou případech. Na té naší to můžeme vyzkoušet. Stačí v Xcode přepnout "View as: Dark Appearance" na světlé téma.

My chceme naše okno pro přidání nové osoby doplnit obrázkem. Protože se jedná o jednobarevný obrázek, tak chceme, aby aplikace pro každé téma použila jiný. To se dá velmi snadno zařídit. Obě varianty obrázku najdete ve zdrojových souborech k této lekci.
Image Set
V první řadě si otevřeme Assets.xcassets
a pomocí
tlačítka +
dole přidáme New Image Set
. Ten z
výchozího Image
přejmenujeme na Person
. Otevřeme
si Attributes inspektor tohoto obrázku a najdeme položku
Apperances
. Ta je ve výchozím stavu nastavena na
None
. My vybereme možnost Any, Dark
.
Hned můžeme vidět, že se změnily "kolonky" pro obrázky a máme možnost přidat obrázek pro tmavé téma:

Zbývá přesunout obrázky:

A to je vše! Nyní již stačí tento obrázek použít a systém automaticky vybere správný podle nastaveného tématu.
Vrátíme se do Main.storyboard
a dokončíme druhé okno.
Image View
Přidáme komponentu Image View
pro zobrazování obrázků a
rovnou ji jako obrázek nastavíme Person
, který jsme si
připravili. Stačí nastavit AutoLayout a bude hotovo.
Image View
tradičně zarovnáme horizontálně na střed,
nastavíme vzdálenost 20
od horní hrany, minimálně
15
od spodní hrany (tedy od textového pole) a maximální výšku
na 150
. Opět platí, že se jedná o návrhy a můžete
samozřejmě experimentovat.
Constraints pro obrázek vypadají takto:

A hotové druhé okno aplikace:

Můžeme také vyzkoušet, že fungují správné obrázky:

Propojení UI a kódu
Uživatelské rozhraní druhého okna je hotové, nyní ho propojíme s
kódem. Potřebujeme @IBOutlet
pro Text Field
a
Date Picker
. Pro tlačítko Přidat vytvoříme
@IBAction
. Jedná se o opakování, v
AddPersonViewController
bychom měli mít:
@IBOutlet var nameTextField: NSTextField! @IBOutlet var datePicker: NSDatePicker! @IBAction func confirmBtn_Clicked(_ sender: NSButton) { }
Komunikace mezi okny
Na závěr této lekce si obě okna propojíme, abychom v té další mohli pohodlně začít přidávat osoby.
Prvním krokem je vytvoření View Controlleru pro druhé okno. Nezapomeňte
při vytváření souboru vybrat Cocoa Class a na další obrazovce nastavit
jako subclass NSViewController
. Šlo by to i ručně, ale tento
způsob je rychlejší. Jako název zvolíme
AddPersonViewController
. Před potvrzením zkontrolujte, že není
zaškrtnuta možnost "Also create XIB file for user interface". To nechceme.
Nyní musíme do Main.storyboard
a nastavit nově vytvořenou
třídu druhému View Controlleru v jeho Identity inspektoru:

Vrátíme se do AddPersonViewController
a přidáme si
následující vlastnost:
weak var delegateVC: ViewController!
Možností, jak komunikovat mezi okny, je nespočet. My si ukážeme jednoduchou z nich, inspirovanou návrhovým vzorem delegát. Pomocí této vlastnosti získáme přístup k View Controlleru hlavního okna a budeme ho moci informovat o tom, že uživatel vyplnil údaje a chce přidat osobu. Samozřejmě bychom zde mohli využít protokol, ale my tvoříme jednoduchou aplikaci.
Vlastnost musí být weak
, aby mezi oběma View
Controllery nevznikl retain cycle,
kdy se objekty vlastní navzájem a nemůže proto dojít k jejich
dealokaci.
Vrátíme se do ViewController
a přetížíme metodu
prepare()
, která je automaticky zavolána těsně předtím, než
proběhne segue.
Její kostra vypadá takto:
override func prepare(for segue: NSStoryboardSegue, sender: Any?) { }
Zde zjistíme, jestli jde o "AddPersonSegue"
a pokud ano, tak z
něj získáme cílový View Controller, což bude právě náš
AddPersonViewController
a nastavíme mu vlastnost
delegateVC
, čímž oba controllery propojíme:
override func prepare(for segue: NSStoryboardSegue, sender: Any?) { if segue.identifier == "AddPersonSegue" { let addPersonVC = segue.destinationController as! AddPersonViewController addPersonVC.delegateVC = self } }
Víme, jakého typu má být destinationController
, takže
použití as!
je v pořádku a dává smysl. Pak už jen nastavíme
vlastnost delegateVC
.
Našemu ViewController
ještě přidáme následující metodu,
abychom mohli propojení snadno otestovat:
func addPerson(name: String) { print("Adding: \(name)") }
Zbývá zamířit do AddPersonViewController
a doplnit
provizorní kód tlačítka Přidat:
@IBAction func confirmBtn_Clicked(_ sender: NSButton) { delegateVC.addPerson(name: "Test") }
Aplikaci můžete zapnout a zkontrolovat výpis konzole v Xcode. Pomocí
addPerson()
metody později budeme osoby přidávat již
doopravdy.
Oprava pozice druhého okna
Na úplný závěr si opravíme startovní pozici druhého okna, jak máme
slíbeno. Využijeme na to přetíženou metodu viewDidAppear()
.
Nemůžeme použít viewDidLoad()
, protože v tomto stádiu ještě
není plně inicializované okno (i když k tomu název svádí) a nedostali
bychom se k němu.
Pro jistotu nezapomeneme také zavolat
super.viewDidAppear()
.
Základní kostra metody v AddPersonViewController
tedy vypadá
takto:
override func viewDidAppear() { super.viewDidAppear() }
Co vlastně potřebujeme udělat?
Nejdříve získat pozici prvního okna, získat rámeček druhého okna (kvůli rozměrům), vypočítat korektní pozici a nastavit ji druhému oknu. Celá metoda bude vypadat takto:
override func viewDidAppear() { super.viewDidAppear() let mainWindowFrame = delegateVC.view.window!.frame let addPersonFrame = view.window!.frame let yPosition = mainWindowFrame.midY - addPersonFrame.height / 2 let xPosition = mainWindowFrame.midX - addPersonFrame.width / 2 view.window!.setFrameOrigin(CGPoint(x: xPosition, y: yPosition)) }
macOS počítá pozice od levého dolního rohu, zatímco iOS (v UIKit) od levého horního.
Pro korektní pozici tedy zjistíme střední Y a X souřadnice a od každé odečteme polovinu šířky respektive výšky, čímž získáme korektní pozici pro druhé okno.
Můžeme vyzkoušet:

Tímto jsme úspěšně dokončili celé UI, propojili okna mezi sebou a také jsme vyřešili pozicování druhého okna.
V následující lekci, Upomínač narozenin pro macOS - Table View a práce s datem, zprovozníme přidávání osob a oživíme Table View.
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 12x (114.77 kB)
Aplikace je včetně zdrojových kódů v jazyce Swift