Naukę pisania aplikacji rozpoczniemy od przyswojenia podstawowych pojęć wstępujących w wybranym przez nas języku programowania. Poniższy wpis pozwoli nam przerobić elementarne zagadnienia związane z programowaniem w języku Swift. Na koniec lekcji spróbujemy wykorzystać zdobytą wiedzę w praktyce i stworzymy prostą aplikację na iOS.

Co będzie nam potrzebne?

Krok 1: Wprowadzenie do Swift

Pierwszym krokiem na naszej drodze będzie zapoznanie się ze sposobem kontrolowania danych w pisanych przez nas aplikacjach.

Stałe (Constants) i zmienne (Variables)

Stałe i zmienne to konstrukcje programistyczne posiadające wyróżniające je nazwy, przypisany typ danych jaki przechowują oraz przechowywane dane. Taka konstrukcja pozwala w kodzie źródłowym odwoływać się do przechowywanych wartości przy pomocy zadeklarowanej nazwy z pominięciem miejsca przechowywania danych w pamięci. W ten sposób możemy przechowywać np. liczby i ciągi znaków. Zmienne definiowane są za pomocą słowa kluczowego var, a stałe są za pomocą słowa kluczowego let.

var myVariableInt = 123
let myConstansFloat = 3.14159

Typy danych

W języku Swift występuje tzw. typowanie statyczne. Oznacza to, że każda stała i zmienna musi mieć nadany typ danych na etapie kompilacji.

Typ całkowity (Integer)

W Swift zdefiniowano osiem typów danych przeznaczonych do reprezentacji liczb całkowitych – są to Int8, Int16 Int32, Int64 w wersjach ze znakiem (signed) oraz bez znaku (unsigned). Liczba całkowita Int bez przypisanej wielkości bitowej przyjmie rozmiar 32 bity na platformach 32-bitowych i 64 bity na platformach 64-bitowych . Podobnie zachowuje się UInt, czyli liczba całkowita bez znaku (unsigned).

Typ Wartość minimalna Wartość maksymalna
Int8 -128 127
Int16 -32 768 32 767
Int32 -2 147 483 648 2 147 483 647
Int64 -9 223 372 036 854 775 808 9 223 372 036 854 775 807
UInt8 0 255
UInt16 0 65 535
UInt32 0 4 294 967 295
UInt64 0 18 446 744 073 709 551 615

Przykład deklaracji zmiennej Int8:

var myInt: Int8 = 123
Typ zmiennoprzecinkowy (Float)

Swift zapewnia trzy typy danych numerycznych reprezentujące liczby zmiennoprzecinkowe: Float dla 32-bitowych liczb zmiennoprzecinkowych, Double (alias Float64) dla 64-bitowych liczb zmiennoprzecinkowych i Float80 (extended-precision) dla 80-bitowych liczb zmiennoprzecinkowych.

var myFloat: Float = 123.123
Typ znakowy (Character)

Znakowy typ danych Character to typ danych reprezentujący jeden znak tekstowy.

var myCharacter: Character = "A"
Typ tekstowy (String)

Typ tekstowy String jest to typ danych służący do przechowywania ciągu znaków. W rzeczywistości String to uporządkowana kolekcja znakówCharacter.

var myString: String = "Tekst"
Typ logiczny (Bool)

Typ logiczny Bool to zbiór wartości logicznych, składający się z dokładnie dwóch elementów: prawda true i fałsz false. Działa on na zasadzie przełącznika ON/OFF.

var myBool: Bool = true
Alias typu (Type Aliases)

W Swiftcie możemy zdefiniować alternatywną nazwę dla istniejącego typu danych za pomocą słowa kluczowego typealias:

typealias myCustomTypeName = Float
var myValue: myCustomTypeName = 123.123

W praktyce alias typu możemy wykorzystać do stworzenia typu danych Point (z pomocą struktury Tuples):

typealias Point = (x: Int, y: Int)
let myPoint:Point
myPoint.x = 1
myPoint.y = 1

Deklaracja

Jak już wcześniej wspomniałem w języku Swift występuje typowanie statyczne, czyli nadawanie typów zmiennym i stałym w czasie kompilacji programu. Typ zmiennym i stałym możemy nadać, podczas ich deklaracji poprzez wskazanie nazwy typu po nazwie zmiennej lub stałej poprzedzając ją znakiem :, tzw. adnotacja typu Type Annotations:

let myConstant: Int
let myFloat: Float
var myVariable: String
var myBool: Bool

Drugim sposobem jest skorzystanie z tzw. inferencji typów, czyli mechanizmu który zwalnia programistę z obowiązku nawania typów i przerzuca obowiązek identyfikacji typów na kompilator. W takim wypadku koniecznym jest przypisanie wartości, na podstawie której kompilator rozpozna właściwy typ:

let myConstant= 1
let myFolat = 123.123
var myVariable = "jeden"
var myBool = true

Możemy również użyć initializera:

let myConstant = Int(1)
let myFloat = Float(123.123)
var myVariable = String("jeden")
var myBool = Bool(true)

Aby szybko sprawdzić jaki typ ma zmienna lub stała wciskamy klawisz alt i klikamy na jej nazwę.

Krotki (Tuples)

Krotka to struktura danych będąca uporządkowanym ciągiem wartości o różnych typach danych. Jako przykład przedstawimy krotkę przechowującą numer i opis błędu HTTP:

var myHttp404Error: (errorCode: Int, errorMessage: String)
myHttp404Error = (404, "Not Found")

Kolekcje (Collections)

Kolekcja jest to konstrukcja programistyczna grupującą elementy danych i pozwalająca traktować je w kodzie jak jeden zbiór danych. W Swift znajdziemy trzy typy kolekcji: Array, Dictionary i Set. Ten ostatni dodany w Swift 1.2.

Tablica (Array)

Tablica Array jest to uporządkowany zbiór elementów, z których wszystkie muszą być tego samego typu. Słowo uporządkowany oznacza w tym przypadku, że każdy element ma przypisany unikatowy indeks. Dla przykładu stworzymy tablicę zawierającą nazwy potraw zapisanych jakoString:

var myArray: Array <String> = ["Pizza", "Naleśniki", "Bigos"]

Każdemu z elementów tablicy nadany zostaje indeks począwszy od 0.

var myPizza = myArray[0] //pod indeksem 0 kryje się Pizza

Słownik (Dictionary)

Słownik jest to nieuporządkowany typ danych przechowujący pary (unikatowy klucz - Key, wartość - Value). W tym wypadku słowo nieuporządkowany oznacza, że każdy element (wartość) słownika rozpoznawany jest za pomocą przypisanego do niego klucza.

var myDictionary: Dictionary <String,  Int> = ["jeden": 1, "dwa": 2, "sto": 100]

Każdej wartości przypisaliśmy unikatowy klucz String:

var myElement = myDictionary["dwa"] //myElement będzie numerem 2

Zbiór (Set)

Nieuporządkowany typ danych będący zbiorem unikatowych elementów jednego typu. W zbiorze Set elementy nie posiadają przypisanych indeksów jak w tablicy ani kluczy jak w słowniku. Oto przykład użycia Set:

var mySet: Set <String> = ["Pizza", "Naleśniki", "Bigos"]

Możemy teraz np. sprawdzić czy nasz zabiór mySet zawiera element "Naleśniki":

var didIHavePancakes = mySet.contains("Naleśniki") // Bool didIHavePancakes równa się true

Pętle (Loops)

Pętla to konstrukcja programistyczna pozwalająca na cykliczne wykonywanie ciągu instrukcji określoną liczbę razy, do momentu zajścia pewnych warunków, dla każdego elementu kolekcji lub w nieskończoność.

For-In

Pętla for-in to rodzaj pętli, której wykonanie polega na powtarzaniu kolejnych iteracji dla wszystkich elementów agregatu danych takiego jak np. zbiór liczb, elementy w kolekcji lub pojedyncze znaki w ciągu znaków. Pętla taka automatycznie przed przejściem do wykonania kolejnej iteracji przypisuje zadanej w nagłówku pętli zmiennej sterującej wartość kolejnego elementu. Dla przykładu stworzymy tablicę Array zawierającą 4 imiona. Następnie za pomocą pętli for-in ze zmienną sterującą specificName wyświetlimy na ekranie tekst z przywitaniem.

let someNames: Array <String> = ["Anna", "Michał", "Tomasz", "Paweł"]
for specificName in someNames {
    println("Cześć, \(specificName)!")
}

Pętla wykona się 4 razy (dla każdego imienia specificName w tablicy imion someNames), wyświetlając komunikat z przywitaniem.

For

Klasyczna pętla for to rodzaj pętli, której wykonanie polega na powtarzaniu kolejnych iteracji dopóki warunek zawarty w pętli jest spełniony. Pętla automatycznie przed przejściem do wykonania kolejnej iteracji przypisuje zadanej w nagłówku pętli zmiennej sterującej wartość wynikającą z wykonanej operacji. Dla przykładu zdefiniujmy pętle for gdzie zmienna index przyjmie wartość inicjacyjną 0. Warunkiem działania pętli będzie index > 3, a operacją wykonywaną po każdej iteracji będzie ++indeks (++operator arytmetyczny zwiększający wartość o 1, tzw. inkrementacja):

var index: Int
for index = 0; index < 3; index++ {
    println("Index = \(index)")
}
// index = 0
// index = 1
// index = 2

While

Pętla whilepowtarza instrukcje dopóki warunek w niej zawarty jest spełniony (true).

var myNumber: Int = 0
while myNumber < 1000 {
    myNumber++
}

Pętla wykona instrukcję 1000 razy.

Do-While

Pętla do-while różni się od while tym, że warunek w niej zawarty sprawdzany jest na jej końcu, co skutkuje wykonaniem instrukcji zawartej w pętli co najmniej raz, nawet jeżeli warunek zawarty w pętli, nie jest spełniony.

var myNumber: Int = 1000
do {
    myNumber++
}
while myNumber < 1000

Pętla wykona instrukcję jeden raz (zwiększając myNumber o jeden) pomimo, iż warunek w niej zawarty nie został spełniony.

Instrukcje warunkowe (Conditional Statements)

Instrukcja warunkowa to konstrukcja programistyczna pozwalająca na wykonanie określonego kodu w zależności od tego czy zdefiniowany warunek (wyrażenie logiczne) jest prawdziwy, czy fałszywy.

If

Instrukcja warunkowa if umożliwia warunkowe wykonanie określonego bloku kodu.

let myAge: Int = 28
if myAge == 28 {
    println("Tak, masz 28 lat!")
}

Powyższa instrukcja sprawdza (za pomocą operatora porównania ==) czy stała myAge równa się 28. Jeżeli wynik jest pozytywny wyświetla zdefiniowany komunikat.

If-Else

Instrukcja warunkowa if-else umożliwia warunkowe wykonanie określonego bloku kodu a jeśli warunek nie jest spełniony alternatywnegoelse bloku kodu.

let myAge: Int = 23
if myAge == 28 {
    println("Tak, masz 28 lat!")
} else {
    println("Nie, nie masz 28 lat!")
}

Pętla tak jak poprzednio sprawdza czy stała myAge równa jest 28, jeśli nie to wyświetla alternatywny komunikat.

Możemy tworzyć w ten sposób również bardziej skomplikowane wyrażenia logiczne. Na przykład wykorzystując konstrukcję else if do postawienia dodatkowych warunków:

let myAge: Int = 16
if myAge == 28 {
    println("Tak, masz 28 lat, więc jesteś pełnoletni!")
} else if myAge > 18 {
    println("Jesteś pełnoletni ale nie masz 28 lat!")
} else {
    println("Nie, nie masz 28 lat i nie jesteś pełnoletni!")
}

Instrukcja wyboru (Switch statement)

Instrukcja wyboru to konstrukcja programistyczna umożliwiająca wybór wykonywanego kodu na podstawie wartości wyrażenia.

Switch

Instrukcja switch umożliwiająca wybór bloku kodu do wykonania spośród wielu opcji. Instrukcja sprawdza czy zachodzi któryś z określonych w niej przypadków case i wykonuje odpowiedni dla niego blok kodu. W Swift instrukcja switch musi zawierać wyrażenie domyśle default, które wykonane zostanie w przypadku braku pasujących przypadków case.

var myNumber: Int = 1
switch myNumber{
case 1:
    println("Jedynka")
case 2:
    println("Dwójka")
case 3:
    println("Trójka")
default:
println("Nie ma takiego numeru")
}

Switch-where

Zastosowanie słowa kluczowego where w instrukcji switch pozwala na dodanie dodatkowych warunków dla określonych przypadków.

var myNumber: Int = 2
switch myNumber{
case let number where number != 1:
    println("To nie jedynka!")
    fallthrough
case 2:
    println("Dwójka")
case 3:
    println("Trójka")
default:
    println("Nie ma takiego numeru")
}

Za pomocą instrukcji switch możemy np. stworzyć prosty kalkulator BMI:

let myHeight: Float = 1.68 // Tu wprowadzamy nasz wzrost w m
let myWeight: Float = 66 // Tu wprowadzamy naszą wagę w kg
var myBMI: Float

myBMI = myWeight / (myHeight*myHeight) // Obliczamy BMI

switch myBMI {
case 0..<16:
    println("Zjedz coś szybko!")
case 16..<17:
    println("Lepiej skonsultuj się z lekrzem.")
case 17..<18.5:
    println("Jedz więcej!")
case 18.5..<25:
    println("Jesteś ideałem <3")
case 25..<30:
    println("Ostatnie ptasie mleczko!")
case 30..<35:
    println("Idź na rower.")
case 35..<40:
    println("Bierz rower na plecy i idź pobiegać!")
case let bmi where bmi>40:
    println("Czy ktoś wsiada z Tobą do windy?")
default:
println("Coś poszło nie tak.")
}

Użyty powyżej zapis 0..<16 to tzw. half-open range operator, definiujący przedział prawostronnie otwarty zawierający liczby od 0 do 16.

Słowa kluczowe continue, break, fallthrough

Continue

Słowo kluczowe continue umieszczone w pętli lub instrukcji sterującej, powoduje rozpoczęcie nowej iteracji i zaprzestanie wykonywania dalszych instrukcji dla obecnej iteracji.

Break

Zastosowanie słowa kluczowego breakw pętli lub instrukcji sterującej powoduje przerwanie jej wykonywania.

Fallthrough

Słowo kluczowe fallthroughpowoduje przejście do następnego przypadku case w instrukcji switch.

Krok 2: Wykorzystanie zdobytej wiedzy w praktyce

Kto nie pisze programów ten nie nauczy się pisać programów! Zgodnie z tą maksymą przygotujemy naszą nową aplikację na iOS. Zadaniem naszej aplikacji będzie nakładanie filtrów na obrazek.

Nowy projekt

Projekt tworzymy analogicznie do projektu w pierwszym kroku lekcji 0 z tą różnicą, że tym razem nasz projekt nazwiemy "MySecondApp".

Dodajemy zasoby

W naszym nowym projekcie potrzebować będziemy obrazka, na który nakładać będziemy filtry. Ja wybrałem bardzo popularny wśród fanboyów Apple motyw, czyli zdjęcie zegarka Apple Watch. Tak więc pobieramy z internetu zdjęcie smartwatcha w formacie jpg i zmieniamy jego nazwę na "Apple_Watch.jpg". W celu dodania wybranego obrazka do zasobów naszej aplikacji klikamy prawym przyciskiem myszy na głównym folderze naszego projektu w Project navigator i wybieramy Add Files to "MySecondApp"....

alt text

Następnie wskazujemy gdzie znajduje się nasz plik "Apple_Watch.jpg" i zatwierdzamy operację.

Projektujemy interfejs

Mamy już niezbędne zasoby, więc przechodzimy do projektowania interfejsu użytkownika. W utworzonym projekcie, w Project navigator wybieramy plik main.storyboard. Następnie w Library bar wskazujemy zakładkę Objects i wyszukujemy UIImageView. UIImageView jest to kontener służący do wyświetlania obrazków:

alt text

Tak jak nauczyliśmy się w poprzedniej lekcji przeciągamy UIImageView chwytając go kursorem z Library pane i prowadzimy z wciśniętym lewym przyciskiem myszy do naszej aplikacji tak, aby nowy element pojawił się idealnie na środku aplikacji:

alt text

Następie, mając zaznaczony, dodany chwilę wcześniej UIImageView klikamy na Attributes inspector. W polu Image wybieramy nasz plik graficzny "Apple_Watch.jpg". Dzięki temu zabiegowi nasz UIImageView domyślnie wyświetlał będzie zegarek Apple:

alt text

Naszym oczom ukaże się obrazek wypełniający całość aplikacji. Wprawne oko zauważy jednak, że zegarek zrobił się jakiś kwadratowy:

alt text

Kwadratura dodanego do aplikacji Apple Watch spowodowana jest tym, iż obrazek w UIImageView domyślnie skalowany jest do wypełnienia całego widoku. Aby to naprawić musimy w Attributes inspector w polu View wybrać opcję Aspect Fit (dopasowanie obrazka do widoku bez zmiany jego proporcji):

alt text

Kolejnym elementem naszego interfejsu graficznego będzie UIToolbar, czyli pasek narzędziowy. Dodajemy go w taki sam sposób jak UIImageView tj. wyszukujemy jego nazwę w Library bar:

alt text

Następnie przeciągamy UIToolbar do interfejsu naszej aplikacji, tak aby znajdował się na samym dole:

alt text

Pasek UIToolbar domyśle zawiera niepotrzebny nam w tej chwili przycisk z napisem "Item". Aby usunąć niechciany element, zaznaczmy go i z menu wybieramy Edit->Delete:

alt text

Do naszego paska dodamy UISegmentedControl, czyli wielosegmentowy przycisk. Jak wcześniej wyszukujemy go w Library bar:

alt text

Następnie przeciągamy UISegmentedControl w taki sposób, żeby znalazł się na dodanym wcześniej pasku UIToolbar:

alt text

Zaznaczmy nasz nowo dodany element i w Attributes inspector w polu Segments wpisujemy 3. Jak można się domyślić, na ekranie pojawi się trzeci przycisk. Następnie przełączając listę Segment wprowadzamy etykiety Title dla każdego z trzech przycisków. Nazwijmy nasze przyciski "Original", "Gold" i "Blue":

alt text

Ostatnim, niewidocznym elementem interfejsu będzie Flexible Space Bar Button Item, który wyszukujemy w Library bar:

alt text

Przeciągniemy po jednym Flexible Space Bar Button Item po obu stornach dodanego wcześniej UISegmentedControl:

alt text

Dzięki dodaniu Flexible Space Bar Button Item nasze przyciski będą zawsze umiejscawiać się na środku paska UIToolbar. Zobaczmy jak nasz interfejs prezentuje się na docelowych urządzeniach. Klikamy na opisywany w lekcji 0 Assistant editor (dwa połączone kółeczka). Następnie na pasku Jump bar prawego edytora na przycisk Automatic i zmieniamy opcję na Preview.

alt text

W okienku Preview zauważymy znany nam z poprzedniej lekcji problem z dostosowaniem elementów interfejsu do różnych urządzeń. Rozwiążemy go w taki sam sposób jak poprzednio, czyli pozwolimy Xcode samemu dopasować nasz interfejs do różnych rozmiarów urządzeń. W tym celu, przy aktywnym widoku Storyboard w menu wybieramy Edit->Select All (powinny zaznaczyć się wszystkie elementy naszego interfejsu). Następnie klikamy na ikonkę, umieszczoną w prawym dolnym rogu naszego Storyboard i wybieramy opcję Add Missing Constraints:

alt text

Zakończyliśmy projektować interfejs graficzny, więc czas przejść do pisania kodu. Powróćmy do widoku Automatic prawego edytora.

Łączymy interfejs z kodem (IBOutlet i IBAction)

Aby dodać interakcje między obiektem w kodzie a jego graficzną reprezentacją musimy stworzyć dla naszego obiektu tzw. ujście (IBOutlet/IBOutletCollection) lub/i akcję (IBAction). IBOutlet i IBAction są to słowa kluczowe, za pomocą których Interface Builder podłącza interfejs aplikacji do kodu źródłowego. Ujście IBOutlet pozwala na odwołanie się do obiektu w celu, uzyskania dostępu do właściwości obiektu, natomiast akcja IBAction pozwala na określenie, która metoda klasy może być wywoływana bezpośrednio z obiektu którego dotyczy. Akcje lub ujście w Interface Builder dodajemy zaznaczając interesujący nasz element interfejsu i trzymając klawisz ctrl ciągniemy z edytora Storyboard do edytora wyświetlającego kod (pojawi się niebieska linia):

alt text

Po przeciągnięciu niebliskiej linii do kodu źródłowego wyświetli się okienko z kilkoma opcjami. Jako pierwszą stworzymy akcje wykonywaną po naciśnięciu na umieszczone na pasku przyciski. W polu Connection wybieramy opcję Action a w polu Name wpisujemy "colorButton".

alt text

Dzięki temu, będziemy mogli przypisać w kodzie działanie naszym przyciskom. Potrzebujemy również informacji, który przycisk jest wciskany, dlatego powtarzamy operację, tylko tym razem w polu Connection wybieramy opcję Outlet, a w polu Name wpisujemy "colorSelect".

alt text

Ponieważ nasza aplikacja ma nakładać filtry na obrazek, musimy uzyskać do niego dostęp. Dostęp do naszego obiektu uzyskamy poprzez dodanie ujścia IBOutlet dla umieszczonego w aplikacji elementu UIImageView. W tym celu powtarzamy jeszcze raz całą operację, tym razem wyprowadzając niebieską linię z naszego obrazka. Jako nazwę wpisujemy "mainImage":

alt text

Technologia Core Image

Do pokolorowania naszego zegarka użyjemy technologii Core Image. Core Image jest to technologia służąca do analizy i przetwarzania obrazu oraz video w czasie rzeczywistym. Naszym pierwszym ruchem będzie utworzenie CIContext, czyli kontekstu do renderowania naszego obrazka. Ponad outletami stworzymy stałą CIContex o nazwie contex wprowadzając let context = CIContext(options: nil). Utworzymy też obiekt inputImage odpowiedzialny za przechowywanie oryginalnego obrazka. Dla naszego obiektu inputImage pobierzemy z zasobów dodany wcześniej obrazek "Apple_Watch.jpg".

import UIKit

class ViewController: UIViewController {

    let context = CIContext(options: nil)
    let inputImage = CIImage(image: UIImage(named: "Apple_Watch.jpg"))
    @IBOutlet var mainImage: UIImageView!
    @IBOutlet var colorSelect: UISegmentedControl!
    @IBAction func colorButton(sender: AnyObject) {
...

Kodujemy

Następnie w bloku kodu przypisanego do wcześniej utworzonej akcji utworzymy obiekt filteredImage, który przechowywał będzie obrazek po nałożeniu filtrów.

import UIKit

class ViewController: UIViewController {

    let context = CIContext(options: nil)
    let inputImage = CIImage(image: UIImage(named: "Apple_Watch.jpg"))
    @IBOutlet var mainImage: UIImageView!
    @IBOutlet var colorSelect: UISegmentedControl!
    @IBAction func colorButton(sender: AnyObject) {

        var filteredImage: CIImage
    ...

Kolejną częścią naszego kodu będzie opisywana wyżej instrukcja switch, za pomocą której przypiszemy kod dla każdego z trzech przycisków umieszczonych przez nas w UIToolbar. W tym celu koniecznym jest pobranie informacji, który z przycisków jest obecnie aktywny, za pomocą wartości selectedSegmentIndex utworzonego wcześniej ujścia colorSelect. Dla wartości 0, będącej indeksem przycisku "Orginal" oraz dla domyślnej wartości defaultna obrazek nie zostanie nałożony żaden filtr, tak więc wpisujemy filteredImage = inputImage. Dla przypadków z indeksem 1 i 2, odpowiednio "Gold" i "Blue" zapiszemy kod jak poniżej:

...
 @IBAction func colorButton(sender: AnyObject) {

        var filteredImage: CIImage

        switch colorSelect.selectedSegmentIndex
        {
        case 0:
            filteredImage = inputImage
            // Przekazujemy obrazek bez filtra
        case 1:
            let filterColor = [kCIInputColorKey: (CIColor(string: "0.8 0.6 0.1 1.0"))]
           filteredImage = inputImage.imageByApplyingFilter("CIColorMonochrome", withInputParameters: filterColor)
            // Nakładamy filtr z kolorem złotym
        case 2:
            let filterColor = [kCIInputColorKey: (CIColor(string: "0.0 0.7 0.9 1.0"))]
            filteredImage = inputImage.imageByApplyingFilter("CIColorMonochrome", withInputParameters: filterColor)
            // Nakładamy filtr z kolorem niebieskim
        default:
             filteredImage = inputImage
            // Przekazujemy obrazek bez filtra
        }
...

Stała filterColor reprezentuje CIColor z kluczem kCIInputColorKey, który wykorzystany zostanie w nakładanym filtrze. W naszym przypadku pierwszym kolorem jest złoty "0.8 0.6 0.1 1.0" (kolor RGB, gdzie 80% czerowny, 60% zielony, 10% niebieski, oraz 100% pokrycia (alpha). Drugi kolor to kolor niebieski. Metoda imageByApplyingFilter zwraca nowy obraz stworzony przez zastosowanie określonego filtra do oryginalnego obrazka. My zastosujemy filtr CIColorMonochrome. Ostatnim krokiem jest wyrenderowanie naszego obrazka i przekazanie go do interfejsu graficznego:


... 
        // Renderuj obrazek
        let renderedImage = context.createCGImage(filteredImage, fromRect: filteredImage.extent())

        // Wyświel gotowy obrazek
        mainImage.image = UIImage(CGImage: renderedImage)
...

Pełny kod źródłowy znajdziecie poniżej:

import UIKit

class ViewController: UIViewController {

    let context = CIContext(options: nil)
    let inputImage = CIImage(image: UIImage(named: "Apple_Watch.jpg"))

    @IBOutlet var mainImage: UIImageView!
    @IBOutlet var colorSelect: UISegmentedControl!
    @IBAction func colorButton(sender: AnyObject) {

        var filteredImage: CIImage

        switch colorSelect.selectedSegmentIndex
        {
        case 0:
            filteredImage = inputImage
            // Przekazujemy obrazek bez filtra
        case 1:
            let filterColor = [kCIInputColorKey: (CIColor(string: "0.8 0.6 0.1 1.0"))]
           filteredImage = inputImage.imageByApplyingFilter("CIColorMonochrome", withInputParameters: filterColor)
            // Nakładamy filtr z kolorem złotym
        case 2:
            let filterColor = [kCIInputColorKey: (CIColor(string: "0.0 0.7 0.9 1.0"))]
            filteredImage = inputImage.imageByApplyingFilter("CIColorMonochrome", withInputParameters: filterColor)
            // Nakładamy filtr z kolorem niebieskim
        default:
             filteredImage = inputImage
            // Przekazujemy obrazek bez filtra
        }

        // Renderuj obrazek
        let renderedImage = context.createCGImage(filteredImage, fromRect: filteredImage.extent())

        // Wyświel gotowy obrazek
        mainImage.image = UIImage(CGImage: renderedImage)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

Podziwiamy

Możemy teraz uruchomić naszą aplikację na symulatorze. Ja jednak wybrałem iPada:

Podsumowanie

To już koniec pierwszej lekcji. Mam nadzieję, że wiedza którą próbowałem wam przekazać znalazła sobie cieplutkie miejsce w waszych szarych komórkach. W tej lekcji poznaliśmy absolutne podstawy Swifta i możemy już na własną rękę zacząć zabawę w playground. Jako zadanie domowe polecam napisanie kilku prostych programów (podobnych do przykładu z kalkulatorem BMI), w ten sposób oswoimy się z klepaniem kodu. W następnej lekcji odpowiemy sobie na dwa pytania: czym jest programowanie obiektowe, a czym strukturalne oraz co to jest MVC (Model-View-Controller) i jak to w praktyce działa.