Kurs Swift - Lekcja 1: Podstawy języka
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?
Komputer Mac z zainstalowanym Xcode.
Wiedza zdobyta w Lekcji 0.
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 while
powtarza 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 break
w pętli lub instrukcji sterującej powoduje przerwanie jej wykonywania.
Fallthrough
Słowo kluczowe fallthrough
powoduje 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"....
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:
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:
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:
Naszym oczom ukaże się obrazek wypełniający całość aplikacji. Wprawne oko zauważy jednak, że zegarek zrobił się jakiś kwadratowy:
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):
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:
Następnie przeciągamy UIToolbar do interfejsu naszej aplikacji, tak aby znajdował się na samym dole:
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:
Do naszego paska dodamy UISegmentedControl, czyli wielosegmentowy przycisk. Jak wcześniej wyszukujemy go w Library bar:
Następnie przeciągamy UISegmentedControl w taki sposób, żeby znalazł się na dodanym wcześniej pasku UIToolbar:
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":
Ostatnim, niewidocznym elementem interfejsu będzie Flexible Space Bar Button Item, który wyszukujemy w Library bar:
Przeciągniemy po jednym Flexible Space Bar Button Item po obu stornach dodanego wcześniej UISegmentedControl:
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.
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:
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):
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".
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".
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":
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 default
na 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.