Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
Software
Programování pro iOS - 3. základní ovladače a propojení GUI s kódem
18. srpna 2010, 01.00 | Projekt pro sestavení jednoduché aplikace na iPhone máme hotový a od minule víme, k čemu slouží jednotlivé části projektu a jaké jsou jejich vzájemné vztahy. Na řadě je Interface Builder a úprava aplikace k našemu obrazu.
Dosud jsme se vůbec nezabývali takovou okrajovou drobností, totiž – co vlastně naše aplikace bude dělat? Pro naše účely je to víceméně jedno; pro začátek, jako úkol velmi jednoduchý, si naprogramujeme "dálkoměr na bouřku" – aplikaci, v níž klepneme na tlačítko když se ukáže blesk, pak znovu ve chvíli, kdy uslyšíme hrom, a aplikace nám spočítá, jak je bouřka daleko.
Podobná věc mimochodem již v App Store je; ta naše bude, jak uvidíme, o poznání lepší – ačkoli bez tak výrazné grafiky, takže v konkurenčním boji bychom se asi neuplatnili.
Nastavení atributů cíle
Ačkoli dnes budeme pracovat převážně v Interface Builderu – hlavně s objektovou sítí, reprezentující grafické uživatelské rozhraní v hlavním rámci naší aplikace –, začneme ještě krátce v Xcode nastavením atributů cíle; to je první věc, kterou je obvykle vhodné udělat v libovolném novém projektu.
Cíl, jak víme už z prastarého dílu věnovaného Xcode (kde jsme mu říkali anglicky "target"), v projektu representuje právě aplikaci, již sestavujeme (v jediném projektu bychom klidně mohli mít aplikací více, nebo třeba aplikaci a její pomocnou knihovnu apod.); je tedy logické, že právě zde určíme důležité informace o výsledné aplikaci jako celku. Najdeme tedy cíl ve skupině "Targets" – je tam jen jediný, takže je to velmi snadné – a otevřeme jej poklepáním; alternativně můžeme použít příkaz z hlavní nabídky "Project / Edit Active Target". Xcode otevře odpovídající inspektor, a my jej přepneme do režimu "Properties".
Standardně vyplněné "com.yourcompany" v řádku "Identifier" zde změníme na naše vlastní reversní DNS (např. tedy "cz.mujmac"); to je velmi důležité, protože právě "Identifier" aplikaci jednoznačně identifikuje. Je proto velmi důležité, aby byl pro každou aplikaci unikátní a odlišný od identifikátorů všech ostatních aplikací.
Prozatím zde není nic jiného zapotřebí. Povšimněme si ale řádku "Main Nib File: MainWindow" – právě zde je určeno, že to první, co po spuštění aplikace udělá, bude načtení objektové sítě ze souboru "MainWindow.nib". Pokud bychom z nějakého důvodu chtěli jméno tohoto souboru změnit, můžeme to udělat zde.
Práce s objektovou sítí
Nyní již se můžeme pustit do práce na grafickém uživatelském rozhraní. Připomeňme, že – jak jsme se už ostatně zmínili minule, ačkoli jsme to příliš nezdůrazňovali:
- ovladače, jež tvoří grafické uživatelské rozhraní aplikace, jsou téměř vždy uloženy v předem připravených objektových sítích (není to nutné a GUI lze sestavovat i programově; to je ale obecně velmi nešikovné a je to zapotřebí jen naprosto výjimečně);
- tyto sítě jsou uloženy v projektu v souborech v příponou .xib, jež můžeme zpracovávat pomocí speciální aplikace Interface Builder (v budoucnosti je bude editovat přímo Xcode);
- do výsledné aplikace jsou uloženy ve zkomplikované podobě s příponami .nib; odtud jsou pak objekty přímo načítány do paměti za běhu aplikace.
Jak víme, základní objektová síť "MainWindow" je díky projektovému vzoru sestavena tak šikovně, že její obsah můžeme ponechat beze změny. Z pilnosti se na ni ale podíváme, abychom lépe rozuměli tomu, jak aplikace v iOS fungují; poklepeme tedy v Xcode na soubor "MainWindow.xib". To spustí aplikaci InterfaceBuilder a síť v ní otevře.
Ačkoli jsme si aplikaci Interface Builder již popsali v témže dílu našeho seriálu, v němž jsme si objasnili základy objektových sítí jako takových, budeme se jí dnes věnovat trochu podrobněji, a to ze dvou důvodů: předně, rozdíly mezi tehdejším a dnešním Interface Builderem jsou poměrně značné; kromě toho jsou poměrně podstatné i rozdíly mezi objektovou sítí pro iOSu a pro Mac OS X.
Interface Builder nám otevře okno, jehož obsah bude – po případném přepnutí do hierarchického režimu, vizte ovladač v levém horním rohu – vypadat asi nějak takto:
Položky uvnitř okna reprezentují jednotlivé objekty:
- File's Owner je pouze zástupce objektu – reprezentuje objekt, který není uvnitř objektové sítě, ale mimo ni. Takřka vždy jde o ten objekt, který načtení sítě inicializoval, nebo který tuto síť řídí; u hlavní aplikační sítě je to jednoduché, zde je "File's Owner" vždy aplikace sama (tedy instance třídy UIApplication nebo její podtřídy, representující aplikaci v objektové struktuře);
- First Responder je speciální zástupce, který je velmi důležitý v Mac OS X, ale v iOS jej můžeme většinou zanedbat;
- (Jméno projektu) App Delegate už je skutečný objekt uložený uvnitř naší objektové sítě – jde o instanci delegáta aplikace. Právě zde je vytvořena; v paměti se objeví ve chvíli, kdy tuto objektovou síť aplikace načte – není tedy zapotřebí nikde delegáta vytvářet programově. Za chvilku se k tomuto objektu ještě vrátíme;
- Window je také skutečný objekt uložený uvnitř naší objektové sítě, konkrétně hlavní (a jediné) okno aplikace;
- Konečně pak Main View Controller je třetí (a poslední) objekt, uložený v této síti. Podobně jako tomu bylo u delegáta, i toto je instance třídy, jež je součástí projektu – zde třídy MainViewController. A opět je tento řídicí objekt vytvořen načtením NIBu, a opět jej tedy není třeba vytvářet programově (takže nikde ve zdrojových kódech nenalezneme nic jako "[MainViewController alloc]").
Za zmínku stojí malý zelený čudlík vlevo dole: pokud není zelený, znamená to, že je něco špatně, a nepodařilo se správně synchronizovat zdrojové soubory s odpovídajícími objekty v Interface Builderu; pak je načase se v Xcode podívat, kde je chyba.
Označíme-li některý z objektů v hlavním okně, zobrazí se jeho atributy v okně inspektoru; je-li zavřené, můžeme je otevřít pomocí ikonky "i" ve standardním toolbaru okna nebo příkazem "Tools / Inspector". Inspektor je rozdělen do čtveřice záložek; v prvé z nich jsou atributy zvoleného objektu (to si ukážeme za chvilku), ve druhé vazby, ve třetí rozměry a ve čtvrté identita.
Atributy a rozměry jsou asi zřejmé (a pro naše objekty "delegát aplikace" a "řídicí objekt hlavního rámce" ani nemají smysl), co ale ty druhé dva?
Vazby jsou snad na Interface Builderu to nejdůležitější: umožňují totiž velmi obecné vzájemné vazby a propojení mezi objekty sestavovat přímo v objektové síti, aniž by bylo zapotřebí je navazovat programově. Označíme-li delegáta aplikace a podíváme-li se na jeho vazby v inspektoru, uvidíme toto:
Připomeňme si kód delegáta volaný po spuštění aplikace, který jsme si ukázali minule:
// podstatná část souboru ...AppDelegate.m:
... zatím nedůležité ...
-(BOOL)application:
(UIApplication*)application
didFinishLaunchingWithOptions:
(NSDictionary*)launchOptions {
[window addSubview:mainViewController.view];
[window makeKeyAndVisible];
return YES;
}
... zatím nedůležité ...
Tehdy nám ještě nebylo zřejmé, jak je zajištěno, aby v proměnné view byl odkaz na okno a v proměnné mainViewController aby byl odkaz na řídicí objekt hlavního rámce; teď už to ale vidíme – tyto odkazy jsou součástí objektové sítě, jsou uloženy v souboru "MainWindow.xib" (resp. uvnitř aplikace pak po překladu do binární komprimované formy "MainWindow.nib") a samozřejmě se načtou do paměti spolu s ním.
Stejně tak zde vidíme, jak to, že je náš objekt skutečně delegátem aplikace – prostě a jednoduše je v objektové síti vazba "delegate" z objektu (resp. zástupce objektu) "File's Owner" na naši instanci. Jelikož "File's Owner" zde reprezentuje aplikaci, bude po načtení sítě její atribut "delegate" obsahovat odkaz na náš objekt.
Identita pak – obrázek sem vkládat nebudeme, ale přepněte si na ni inspektor sami, je to šedé "i" na čtvrtém místě – obsahuje jako nejdůležitější údaj jméno třídy: to, že např. řídicí objekt hlavního rámce je instancí třídy MainViewController a žádné jiné, to je nastaveno právě zde, v inspektoru identity.
Grafické uživatelské rozhraní
Pusťme se ale do práce – zavřeme "MainWindow" a otevřeme namísto něj "MainView.xib"; zde nějaké ty změny dělat samozřejmě budeme. Jeho obsah je jednodušší; kromě "File's Ownera" a "First Controlleru" (tyto dva zástupci jsou zobrazeny v Interface Builderu vždy pro jakoukoli objektovou síť) zde je jen jediný objekt, "View" – náš hlavní rámec. Na rozdíl od minula, kdy žádný z objektů neměl další vnitřní strukturu, rámce mohou obsahovat libovolně mnoho vnořených objektů; podíváme-li se dovnitř, uvidíme tam "Light Info Button" – to je tlačítko "i", jež nám do sítě vložil automaticky projektový vzor.
Pro začátek budeme v grafickém uživatelském rozhraní aplikace potřebovat pouze dva objekty:
- tlačítko, jehož prostřednictvím budeme aplikaci ovládat (jedno stisknutí při blesku, druhé při hromu);
- textové pole, v němž zobrazíme výsledek (vzdálenost bouřky).
Pro vkládání ovladačů do rámců nabízí Interface Builder pohodlný grafický režim, v němž vidíme náhled toho, jak bude rámec vypadat. Nejprve si rámec zobrazíme – stačí na něj (tedy na "View" v hlavním okně) poklepat; hned se otevře okno rozměrů odpovídajících obrazovce iPhone, jež má při horním okraji (simulovaný) toolbar a v pravém dolním rohu tlačítko "i".
Otevřeme další okno Interface Builderu, zdrojovou knihovnu ("Tools / Library") – v tomto okně jsou připraveny všechny standardní objekty, jež můžeme do objektových sítí vkládat. Přepneme ji do režimu "Objects / Inputs & Values", a pomocí myši z ní do rámce odvezeme jeden "Label" a jeden "Round Rect Button", nějak takto:
Jejich velikosti pomocí myši upravíme vhodným způsobem; poklepeme na tlačítko a do něj vepíšeme vhodný text (třeba "Stiskni při blesku i hromu").
Pak klepneme na textové pole, v inspektoru aktivujeme režim atributů, a zde nastavíme požadovaný vzhled budoucího zobrazení vzdálenosti – můžeme je třeba vycentrovat, přepnout na bílou barvu písma, vybrat větší font...
Mimochodem, inspektor atributů bere v úvahu dědičnost: jelikož textové pole je dědicem rámce, máme pod inspektorem "Label" další inspektor, nadepsaný "View" – zde bychom mohli upravovat ty atributy, jež textové pole, tedy instance standardní třídy UILabel, dědí od své nadtřídy UIView (co je to nadtřída a dědění?).
Mohlo by to vypadat asi nějak takto:
Více toho prozatím nebudeme potřebovat.
Vazba na kód
Nyní je třeba připravit ve zdrojovém kódu řídicího objektu
- proměnnou, která bude obsahovat (odkaz na) textové pole – takovýmto proměnným běžně říkáme "outlet";
- metodu, která bude obsahovat kód, provedený při stisknutí tlačítka – takovýmto metodám běžně říkáváme "akce",
a propojit je s uživatelským rozhraním pomocí vazeb, o nichž jsme si povídali výše v souvislosti s objektovou sítí "MainWindow".
Nejprve připravíme odpovídající zdrojový kód: vrátíme se do Xcode, otevřeme rozhraní řídicího objektu v souboru "MainViewController.h", a přidáme do něj proměnnou objektu "range", obsahující (odkaz na) objekt třídy UILabel, a deklaraci zprávy "rangeButtonTapped". Proměnnou označíme standardním tagem "IBOutlet", v deklaraci zprávy použijeme jako návratovou hodnotu standardní tag "IBAction"; celé rozhraní bude vypadat nějak takto:
@interface MainViewController:UIViewController
<FlipsideViewControllerDelegate> {
IBOutlet UILabel *range;
}
-(IBAction)rangeButtonTapped;
-(IBAction)showInfo:(id)sender;
@end
Stačí soubor uložit příkazem "File / Save", a Interface Builder změny automaticky vezme v potaz: jestliže v něm nyní označíme "File's Owner" – ten reprezentuje právě řídicí objekt rámce – a přepneme-li inspektor do režimu vazeb ("šipka" na druhém místě), hned v něm uvidíme nový "outlet" i "akci":
Prázdná kolečka při pravém okraji hned také ukazují, že "outlet" je dosud prázdný a ani "akce" není s ničím spojena.
Kde se berou "outlety" searchDisplayController – nepoužitý – a view, a akce showInfo:? Outlety jsou zděděny z nadtřídy UIViewController; akce pak je přímo deklarována v rozhraní a vidíme ji v příkladu výše; jen jsme ji nezapsali sami, ale do zdrojového textu ji za nás zapsal projektový vzor.
Interface Builder nabízí několik možností propojování objektů; v tuto chvíli asi nejjednodušší je táhnout myší přímo z prázdného kolečka při pravém okraji inspektoru "drát" nad objekt, který chceme k outletu nebo akci připojit – textové pole s outletem range tedy propojíme takto:
a analogicky spojíme akci rangeButtonTapped s tlačítkem. Zde se nám ovšem po upevnění "drátu" nabídne paleta možných událostí, protože tlačítko dokáže poslat zprávu na základě řady možných podmínek. Zvolíme třeba "Touch Up Inside" – to znamená, že tlačítko pošle našemu řídicímu objektu zprávu rangeButtonTapped ve chvíli, kdy je uživatel stiskne a opět uvolní.
Vlastní implementace
Zbývá už jen poslední drobnost – musíme naprogramovat požadovanou funkčnost v rámci metody rangeButtonTapped. K tomu samozřejmě můžeme volně využívat všech možností jazyka C, Objective C i standardních knihoven, převážně tedy knihovny Foundation; asi nejjednodušší by to mohlo být takto – kód samozřejmě vložíme v Xcode do implementace třídy v souboru "MainViewController.m":
-(IBAction)rangeButtonTapped {
static NSTimeInterval light=0;
if (light==0) { // lightning
light=[NSDate timeIntervalSinceReferenceDate];
range.text=@"";
} else { // thunder
NSTimeInterval diff=
[NSDate timeIntervalSinceReferenceDate]-light;
light=0;
range.text=[NSString stringWithFormat:
@"%.1 f km",diff*330./1000.];
}
}
A tím jsme hotovi: aplikaci můžeme sestavit a spustit v simulátoru a vše by mělo fungovat korektně.
Zatím ovšem naše aplikace není o nic lepší, nežli ta z App Store; příště ji proto trošku vylepšíme.
Obsah seriálu (více o seriálu):
- Nastal čas na kakao...
- Tak nejdřív kakao ochutnáme...
- Programovací jazyk C: velmi, velmi stručně
- Objective C: to si vysvětlíme podrobněji
- Co jsme si o Objective C ještě neřekli...
- Nastal čas na kakao - Vznik a zánik objektů
- Nastal čas na kakao - Kopírování objektů
- Nastal čas na kakao - Skryté podtřídy
- Nastal čas na kakao - Základní služby objektů
- Nastal čas na kakao - Jak správně psát v Objective C
- Nastal čas na kakao - Jak správně importovat
- Nastal čas na kakao - Podtřídy, delegáti, vkládání, jak se to rýmuje?
- Nastal čas na kakao - Využití kategorií namísto dědičnosti
- Nastal čas na kakao - Vkládání objektů a přesměrování zpráv
- Nastal čas na kakao - Inicializace a rušení objektů
- Nastal čas na kakao - Metody initWith... a designovaný inicializátor
- Nastal čas na kakao - Inicializace: tipy a triky
- Nastal čas na kakao - Accesory: přístup k proměnným instancí
- Nastal čas na kakao - Šedá je teorie, zelený je strom života...
- Nastal čas na kakao - Více o XCode: inspektory
- Nastal čas na kakao - Aplikace RSS2: datový model
- Nastal čas na kakao - Aplikace RSS: implementace datového modelu
- Nastal čas na kakao - Aplikace RSS: parsování XML
- Nastal čas na kakao - Interface Builder a uživatelské rozhraní
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Druhý kontrolér a dokončení aplikace
- Nastal čas na kakao - Drobná vylepšení a zdokonalení...
- Nastal čas na kakao - Ladění
- Nastal čas na kakao - Třídy Foundation Kitu
- Nastal čas na kakao - Třídy Foundation Kitu (2)
- Nastal čas na kakao - Textové řetězce: NS(Mutable)String
- Nastal čas na kakao - Čísla, binární data a další...
- Nastal čas na kakao - Archivace objektů
- Nastal čas na kakao - Trocha magie, aneb distribuované objekty
- Nastal čas na kakao - Málem bychom zapomněli: NSAutoreleasePool
- Nastal čas na kakao - Zpracování výjimek: NSException
- Nastal čas na kakao - NSInvocation a černá magie
- Nastal čas na kakao - Kakao v Tygrovi
- Nastal čas na kakao - Notifikace: nepřímé předávání zpráv
- Nastal čas na kakao - NSUserDefaults
- Nastal čas na kakao - Co nového ve Foundation Kitu
- Nastal čas na kakao – s Intelem, s Intelem, jedeme do...
- Co nového v Xcode
- Začínáme s AppKitem
- Jak MVC v Kakau vypadá doopravdy?
- Jak MVC v Kakau vypadá doopravdy: dokončení
- Přehled tříd AppKitu
- Nastal čas na kakao - Přehled tříd AppKitu 2
- Přehled tříd AppKitu 3: zbývající třídy GUI
- Přehled tříd AppKitu 4: textový systém
- Nastal čas na kakao - Přehled tříd AppKitu 5: hlavně grafika
- Přehled tříd AppKitu 6: dokumentový systém
- Přehled tříd AppKitu 7: dokončení
- Pojmenované vlastnosti objektů
- Pojmenované vlastnosti objektů: implementace
- Pojmenované vlastnosti objektů: relace 1:N
- Pojmenované vlastnosti objektů: řazení jmen a agregační funkce
- Sledování změn objektů
- Sledování změn objektů – ukázka
- Sledování změn objektů – zdrojový kód
- Sledování změn objektů: kód modelu
- Sledování změn objektů: přímý přístup
- Kontroléry a vazby
- Vázání vazeb
- Další vazby s jednoduchým kontrolérem
- Implementace a použití převodu hodnot
- Validace hodnot
- Validace a chyby, a jedna hezká vazba...
- Práce s polem objektů
- Základní vazby NSArrayControlleru
- Převodníky, přepínače, placeholdery
- Mírná vylepšení v mezích zákona
- Objective C 2.0 - novinky z Leoparda
- NSTreeController
- Programování v Cocoa - Pár tipů a triků
- Programování v Cocoa - Základy kreslení
- Kterak nakreslit modrý obdélník...
- Další služby pro kreslení
- Obrázky a písmenka...
- Události a myš
- Lepší práce s myší
- Události klávesnice
- Input Management
- Příkazy a schránka
- Další události
- Táhni a padni
- Byli jsme na tahu; nyní padneme.
- Zvolme si, jak vhodit
- Drobnosti a chybičky
- Speciální případy tahání či házení
- Kterak táhnout něco, co neexistuje?
- Jak na sítě...
- NSURLConnection
- Safari za minutu
- Služby WebKitu
- Kakao v Leopardu
- Druhé Objective C
- Druhé Objective C: různé drobnosti
- Druhé Objective C: kategorie a protokoly
- Druhé Objective C: nový příkaz cyklu
- Druhé Objective C: atributy a accesory
- Druhé Objective C: atributy a accesory
- 64 je dvakrát 32
- Ubicumque dulce est, ibi et acidum invenies...
- Irbis: že prý žádné novinky?
- Blok sem, blok tam, nám už je to všechno jasné...
- Bloky jsou i v AppKitu
- Irbis a Foundation Kit
- Kde jsou má data?
- Kde jsou má data? V NSCache!
- Soubor, jméno, URL, jak se to rýmuje...
- Další podpora NSURL
- Zabíjení!
- A máme tady i...OS!
- Systémové prvky GUI
- Programování pro iOS 1. díl - Rozdíly mezi "i" a "Mac"
- Programování pro iOS - 2. Začínáme programovat
- Programování pro iOS - 3. základní ovladače a propojení GUI s kódem
- Programování pro iOS - 4. Varovná hlášení
- Programování pro iOS - 5. Rámce a jejich řídicí objekty
- Programování pro iOS - 6. Ukládání dat
- Programování pro iOS - 7. Správa paměti a starý restík
- Programování pro iOS - 8. Dokončení aplikace
- Programování pro iOS - 9. Jak dostat aplikaci do iPhone
- Programování pro iOS - 10. Instalace aplikace do cizího iPhone
- Programování pro iOS - 11. Jak dostat aplikaci do libovolného iPhone
- Programování pro iOS - 12. Touching!
- Programování pro iOS - 13. Kreslíme na iPhone
- Programování pro iOS - 14. Udělejme gesto
- Programování pro iOS - 15. Další gesta
- Programování pro iOS - 16. Více prstů, více zábavy
- Programování pro iOS - 17. Podpora standardních gest
- Programování pro iOS - 18. Recognizery v iOS
- Programování pro iOS - 19. Další standardní recognizery
- Programování pro iOS - 20. Co nového v iOSu
- Programování pro iOS - 21. "Multitasking"
- Programování pro iOS - 22. Nulla est honesta avaritia nisi temporis
- Programování pro iOS - 23. Jak se aktivovat, jsme-li v pozadí
- Programování pro iOS - 24. Zbývající drobnosti
- Programování pro iOS - 25. Řídicí objekty rámců
- Programování pro iOS - 26. Jak se dělá UIViewController
- Programování pro iOS - 27. Kde vzít rámce
- Programování pro iOS - 28. Základní služby
- Programování pro iOS - 29. Práce s rámci
- Programování pro iOS - 30. Rotace zařízení
- Programování pro iOS - 31. Správa paměti v rámcích
- Programování pro iOS - 32. Řídicí objekt pro tabulky
- Programování pro iOS - 33. Řídicí objekt pro strom
- Programování pro iOS - 33. Více o UINavigationControlleru
- Programování pro iOS - 35. Ještě jednou UINavigationController
- Programování pro iOS - 36. Po navigátoru taby
- Programování pro iOS - 37. Více o UITabBarControlleru
- Programování pro iOS - 38. Dokončení UITabBarControlleru
- Programování pro iOS - 39. UIPopoverController
- Programování pro iOS - 40. Další triky UIPopoverControlleru
- Programování pro iOS - 41. Zbývající služby UIPopoverControlleru
- Programování pro iOS - 42. UISplitViewController
- Programujeme v
iTunesXcode 4 - Programování pro iOS - 44. Předvolby Xcode 4
- Programování pro iOS - 45. Práce v Xcode 4
- Xcode 4: projekt a cíle
- Xcode 4: práce s cíli
- Xcode 4: Build Settings
- Xcode 4: Build Phases
- Xcode4: Build Phases podruhé
- Xcode 4: Co jsou to Build Rules?
- Xcode4: taje editoru
- Xcode4: automatické doplňování v editoru
- XIBy chyby
- Více o XIBech
- Editor XIBů
- Inspektory pro XIBy
- Vazby mezi objekty v XIBech
- Vazby mezi objekty v kódu
- Paletky Xcode pro XIBy
- Xcode 4: levý sloupec
- Xcode 4: okno Organizer
- Xcode 4: okno Organizer, část druhá
- Xcode 4: co je to Workspace?
- Xcode 4: základy schémat
- Xcode 4: akční schémata