Programování pro iOS - 3. základní ovladače a propojení GUI s kódem - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

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:

Soutěž

Sponzorem soutěže je:

IDIF

 

Kde se narodil známý fotograf František Drtikol?

V dnešní soutěži hrajeme o:

Seriály

Více seriálů



Informace

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):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Tipy a Triky  

 » Rubriky  » Začínáme s  

 » Rubriky  » Software  

 

 

 

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: