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