IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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.

Přepínač na Assistant editor v Xcode - macOS - Desktopové aplikace ve Swift

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:

Propojení UI komponent macOS se Swift kódem v Xcode - macOS - Desktopové aplikace ve Swift

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:

Vytvoření IBaction v Xcode - macOS - Desktopové aplikace ve Swift

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:

Vybrání Number Formatter v Xcode - macOS - Desktopové aplikace ve Swift

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

Localize Format v Xcode - macOS - Desktopové aplikace ve Swift

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:

Swift dialog pro dělení nulou v macOS - macOS - Desktopové aplikace ve Swift

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.

macOS kalkulačka ve Swift - macOS - Desktopové aplikace ve Swift

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 18x (38.85 kB)
Aplikace je včetně zdrojových kódů v jazyce Swift

 

Předchozí článek
Návrh jednoduché kalkulačky pro macOS
Všechny články v sekci
macOS - Desktopové aplikace ve Swift
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 1.-3. lekci vývoje pro macOS
Článek pro vás napsal Filip Němeček
Avatar
Uživatelské hodnocení:
1 hlasů
Autor se věnuje vývoji iOS aplikací (občas macOS)
Aktivity