Lekce 3 - Dokončení jednoduché kalkulačky pro macOS ve Swift
V předchozí lekci, Návrh jednoduché kalkulačky pro macOS, jsme dokončili uživatelské rozhraní naší jednoduché kalkulačky a zbývá ho uvést k životu.
Dnes začneme psát Swift kód. Ještě předtím ale musíme naše komponenty uživatelského rozhraní "napojit" na kód, abychom k nim mohli přistupovat.
Budeme totiž potřebovat číst zadaná čísla, vybranou operaci a také
reagovat na stisk tlačítka. No a samozřejmě nesmíme zapomenout na náš
Label, který zobrazí výsledek.
Propojení UI s kódem
Propojení komponent s kódem funguje na macOS úplně stejně jako na iOS, takže pro mnohé z vás určitě půjde pouze o opakování.
Začneme otevřením Main.storyboard a přepneme na Assistant
editor, který zobrazí dva otevřené soubory vedle sebe. Přepínač můžete
vidět níže.

Nalevo byste měli vidět naše uživatelské rozhraní a vedle soubor
ViewController.swift, který bude obsluhovat právě naše
okno.
Za sebe doporučuji označovat komponenty v Document Outline (to je ten seznam komponent nalevo od náhledu uživatelského rozhraní). Potom za držení klávesy Ctrl táhneme z komponenty do zdrojového souboru, jak je vidět na animaci níže:

Tímto vytvoříme tzv. @IBOutlet, což je speciální
proměnná odkazující na naši komponentu. Při spuštění aplikace bude
automaticky inicializovaná a můžeme pomocí ní číst obsah
Text Field komponenty a dále s ní manipulovat.
Stejným stylem propojíme ještě Combo Box, druhý
Text Field a poslední Label pro zobrazení
výsledku.
Ve zdrojovém kódu VC byste měli mít tyto propojené komponenty:
@IBOutlet var firstInput: NSTextField! @IBOutlet var secondInput: NSTextField! @IBOutlet var mathOperationComboBox: NSComboBox! @IBOutlet var resultsLabel: NSTextField!
Moc nepomáhá, že v kódu nejsou komponenty
Text Field a Label rozlišeny podobně, jako tomu je u
iOS. Jejich automatické nastavení ale vybráním v části s tvorbou UI
zajistí, že se budou chovat korektně, protože ji přednastaví.
Zbývá tlačítko. S ním nebudeme pracovat jako s @IBOutlet,
ale zajímá nás pouze situace, kdy na něj uživatel klikne. Pro to slouží
speciální metody označované jako @IBAction a jejich vytvoření
je dost podobné @IBOutlet, stačí změnit typ při vytváření,
viz opět animace níže:

A máme hotovou metodu, která bude automaticky spuštěna při každém kliknutí na tlačítko. Právě zde budeme řešit výpočet a zobrazovat výsledek.
@IBAction func calculateBtnClicked(_ sender: NSButton) {
}
Úpravy storyboard
Ještě, než se pustíme do implementace naší výpočetní metody,
usnadníme si práci pomocí jednoduché úpravy v Main.storyboard.
Jak možná víte, náš rodný jazyk používá čárku k oddělení desetinné
části čísla, ale angličtina a programovací jazyky používají tečku.
Takže jednoduše našim Text Field komponentám, respektive
jejich formátující části, nastavíme, aby ignorovala lokalizaci. To v
překladu znamená, že bude vždy očekávat tečky v desetinných číslech.
Jinak nám nedovolí potvrdit vstup, stejně jako kdybychom napsali
písmeno.
V Document Outline je nejdříve třeba rozkliknout Text Field
komponentu a dostat se až na Number Formatter, jak můžete vidět
na screenshotu níže:

Potom stačí v Attribute inspektoru odškrtnout volbu "Localize Format".

Drobný problém je vyřešen a můžeme se pustit do implementace naší
metody na výpočet. Co vlastně chceme dělat? V první řadě potřebujeme
zjistit zadaná čísla od uživatele a potom podle vybrané operace v
komponentě Combo Box provést výpočet.
Začneme získáním čísel z Text Field komponent, díky
Number Formatter by měly být vždy validní, ale pro jistotu
využijeme guard a zkusíme text z obou komponent převést na
čísla. Když se to nepovede, tak zkrátka nic dalšího dělat nebudeme:
guard let firstNumber = Float(firstInput.stringValue), let secondNumber = Float(secondInput.stringValue) else {return}
V tomto kroku již víme, že máme k dispozici dvě čísla a můžeme
provést výpočet. Z Combo Box lze jednoduše získat index
zvoleného prvku, takže využijeme jednoduchý switch a rovnou
výsledek nastavíme jako stringValue naší Label
komponentě pro zobrazení výsledku:
switch mathOperationComboBox.indexOfSelectedItem { case 0: resultsLabel.stringValue = "\(firstNumber + secondNumber)" case 1: resultsLabel.stringValue = "\(firstNumber - secondNumber)" case 2: resultsLabel.stringValue = "\(firstNumber / secondNumber)" case 3: resultsLabel.stringValue = "\(firstNumber * secondNumber)" default: resultsLabel.stringValue = "-" }
Další možností by bylo získat z Combo Box
rovnou vybraný prvek. To by se hodilo v případě, že bychom ho potřebovali
nějak zpracovat. Máme dostupnou metodu itemObjectValue(at: ), kde
bychom v parametru využili právě indexOfSelectedItem. Komponenta
neřeší typy, takže tato metoda vrací Any a musíme se postarat
o přetypování, nejlépe přes as?.
Nyní můžete aplikaci zapnout a bude počítat. Pro jistotu si ukážeme kompletní metodu pro tlačítko:
@IBAction func calculateBtnClicked(_ sender: NSButton) { guard let firstNumber = Float(firstInput.stringValue), let secondNumber = Float(secondInput.stringValue) else {return} switch mathOperationComboBox.indexOfSelectedItem { case 0: resultsLabel.stringValue = "\(firstNumber + secondNumber)" case 1: resultsLabel.stringValue = "\(firstNumber - secondNumber)" case 2: resultsLabel.stringValue = "\(firstNumber / secondNumber)" case 3: resultsLabel.stringValue = "\(firstNumber * secondNumber)" default: resultsLabel.stringValue = "-" } }
Naše kalkulačka má jeden drobný problém, v Combo Box není
po zapnutí nic vybrané. To můžeme vyřešit jedním řádkem kódu v metodě
viewDidLoad(), která je zavolána po spuštění aplikace.
Zde jednoduše vybereme třeba první operaci:
override func viewDidLoad() { super.viewDidLoad() mathOperationComboBox.selectItem(at: 0) }
Jestli vám vadí, že se aplikace při zkoušení nezavře při zavření
okna, existuje na to rychlá oprava. Otevřete si AppDelegate.swift
a přidejte tuto metodu. Xcode vám ji případně napoví:
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true }
Její název jednoznačně vystihuje, o co vlastně jde. Jakmile uživatel zavře poslední okno (v našem případě jediné), dojde také k vypnutí aplikace.
Dialog
Možná vás napadlo, co se stane, když se uživatel pokusí dělit
0? Aplikace překvapivě nespadne, ale zobrazí výsledek "inf"
jako infinity neboli nekonečno. Kdybychom jako datový typ používali
Int místo Float, tak aplikace spadne.
Ani jedno není ideální, takže si ukážeme řešení a rovnou také, jak zobrazit informační dialog uživateli.
Připravíme si metodu, která zobrazí tento dialog a informuje uživatele,
že dělit 0 prostě nejde:
func showDivisionByZeroAlert() { let alert = NSAlert() alert.messageText = "Even this cool app cannot divide by 0!" alert.runModal() }
Zobrazení základního dialogu je velmi snadné. Stačí vytvořit instanci
NSAlert a přidat text. Potom už pouze stačí dialog zobrazit.
Bude vypadat takto:

Zbývá tedy vyřešit situaci, kdy se uživatel pokusí dělit
0. Upravíme tedy náš switch v metodě obsluhující
tlačítko, konkrétně případ, kdy je vybráno dělení. Před samotné
dělení umístíme podmínku, kde se jednoduše zeptáme, jestli náhodou není
druhé číslo 0. Pokud ano, tak zobrazíme dialog a dál
nepokračujeme:
case 2: if secondNumber == 0 { showDivisionByZeroAlert() return } resultsLabel.stringValue = "\(firstNumber / secondNumber)"
A to je celé! Máme první funkční macOS aplikaci.

V následujícím cvičení, Řešené úlohy k 1.-3. lekci vývoje pro macOS, si procvičíme nabyté zkušenosti z předchozích lekcí.
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 19x (38.85 kB)
Aplikace je včetně zdrojových kódů v jazyce Swift
