Programování pro iOS - 26. Jak se dělá UIViewController - 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

 

Odkud pochází fotografka Anne Erhard?

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

Seriály

Více seriálů



Software

Programování pro iOS - 26. Jak se dělá UIViewController

26. ledna 2011, 00.00 | Jaké je postavení řídicích objektů rámců, k čemu slouží a proč se bez nich jen těžko můžeme obejít už víme. Dnes se pustíme do jejich konkrétního programátorského rozhraní. A začneme samozřejmě tím základním – jejich výrobou.

Nejzákladnějším – ačkoli, jak víme, zdaleka ne jediným – způsobem, jak vytvořit objekt nějaké třídy, je poslat této třídě standardní zprávu alloc a nově vytvořenou instanci inicializovat pomocí vhodné zprávy init... – to jsme si ukazovali už hodně dávno, a od té doby se nic nezměnilo.

To je s třídou UIViewController jednoduchá: nabízí totiž pouze jediný inicializátor (vedle těch zděděných z nadtříd nebo připojených z protokolů; k tomu se ještě za chvilku vrátíme, ale zde to není příliš podstatné):

-initWithNibName:(NSString*)nibName bundle:(NSBundle*)nibBundle;

Tato metoda korektně inicializuje instanci řídicího objektu rámce a je také jejím designovaným inicializátorem (takže pokud vytváříme ve vlastních podtřídách UIViewController nové metody initWith..., měli bychom v nich použít právě "[super initWithNibName:... bundle:...]", případně bychom, je-li to zapotřebí, měli právě tuto metodu reimplementovat – podrobně jsme si mechanismus designovaných inicializátorů vysvětlili zde).

Oba dva argumenty souvisí s možností načíst kompletní strukturu rámců, spravovanou právě vytvořeným a inicializovaným řídicím objektem, z objektové sítě uložené v souboru NIB: prvý určuje jméno tohoto souboru (příponu ".nib" není třeba uvádět), druhý složku, v níž se má tento soubor hledat – nikoli přímo pomocí její cesty, nýbrž prostřednictvím třídy NSBundle; v daleko nejběžnějším případě, kdy je NIB přímo součástí samotné aplikace, můžeme namísto "[NSBundle mainBundle]" použít hodnotu nil.

Řídicí objekt si obsah tohoto NIBu načte automaticky ve chvíli, kdy to bude zapotřebí: obecně (opět nikoli bez výjimek, ale v naprosté většině případů) platí to, že všechny rámce, spravované jedním řídicím objektem, jsou uloženy v samostatném NIBu, v němž nebývá už nic jiného; tím se ale budeme podrobněji zabývat až příště.

Hodnotu nil můžeme použít i pro argument nibName; má to smysl ve dvou vzájemně různých případech:

• jmenuje-li se soubor NIB stejně jako třída (tj. pracujeme-li např. s třídou "@interface MyVC:UIViewController" a jsou-li odpovídající rámce uloženy v souboru "MyVC.nib") – řídicí objekt se po NIBu tohoto jména dívá automaticky;

• nechceme-li načítat NIB vůbec, typicky proto, že rámce pro grafické uživatelské rozhraní sestavíme také programově (jak to udělat si ukážeme také příště). Pokud totiž byla hodnota argumentu nibName nulová a pokud se soubor NIB se jménem odpovídajícím třídě nepodaří nalézt, řídicí objekt automaticky vytvoří hlavní rámec jako instanci třídy UIView.

Nakonec se ještě můžeme zběžně zmínit o tom, že programátoři Apple (na rozdíl od mnoha programátorů třetích firem) vlastní API dobře znají a korektně zacházejí s designovanými inicializátory; proto můžeme zcela bezpečně používat také inicializátor init, zděděný od třídy NSObject – je zcela ekvivalentní použití zprávy initWithNibName:bundle: s oběma argumenty nil (není-li snad někomu zřejmé, proč tomu tak je, vizte prosím opět popis mechanismu designovaných inicializátorů).

Načtení řídicího objektu z NIBu

Poměrně často namísto programového vytvoření řídicího objektu využíváme schopnost objektových sítí NIB obsahovat objekty libovolných tříd (nejen tedy rámce grafického uživatelského rozhraní), a řídicí objekt vytváříme v odpovídajícím zdrojovém souboru XIB; jeho instance se tedy vytvoří načtením odpovídajícího NIBu (který, jak víme, vznikne překladem XIBu při sestavování aplikace) do paměti. Právě tak tomu je i v řadě projektových vzorů Xcode a právě tak tomu bylo i v našem bouřlivém příkladu, jehož vnitřní strukturu jsme si podrobně popsali zde.

Chceme-li do nějakého NIBu řídicí objekt rámce sami uložit, je to velmi jednoduché:

• v panelu "Library" najdeme "View Controller" – můžeme pro lepší přehled panel pomocí rozevírací nabídky v jeho horní části přepnout do režimu "Controllers" – a vhodíme jej myší do hlavního okna XIBu;

• zde tento objekt označíme, panel inspektoru přepneme do režimu "Identity", a v poli "Class" zapíšeme (nebo vybereme z rozevírací nabídky) jméno třídy, jejíž má být tento řídicí objekt instancí (např. tedy "MyVC" v minulém příkladu "@interface MyVC:UIViewController", pokud bychom řídicí objekt vkládali do NIBu).

Ačkoli je samozřejmě v principu možné, aby rámce, jež řídicí objekt spravuje, byly uloženy v témže NIBu, v němž je sám tento řídicí objekt, v praxi se to z řady dobrých důvodů nedělává. Je-li tedy sám řídicí objekt uložen v nějakém NIBu, jsou jeho rámce typicky v NIBu jiném (jehož obsah si řídicí objekt načte automaticky ve chvíli, kdy to bude zapotřebí, přesně stejně, jako kdybychom jej vytvořili programově podle minulého odstavce).

Pokud se tento NIB jmenuje stejně jako třída, jíž je řídicí objekt instancí, o nic se starat nemusíme – takový NIB se načte automaticky, stejně, jako když jsme při programovém sestavování použili nil v argumentu nibName. Co ale v případě, kdy máme rámce uloženy v NIBu jiného jména?

V tomto případě je třeba přepnout panel inspektoru do režimu "Attributes"; zde nalezneme jako předposlední položku textové pole "NIB name", a do něj zapíšeme požadované jméno. Toto textové pole je tedy při načítání řídicího objektu z NIBu ekvivalentem argumentu nibName při vytváření programovém.

Pro úplnost poznamenejme, že pro sestavování struktury založené na řídicích objektech pro "navigační" nebo "tabbarovou" aplikaci máme k dispozici v Interface Builderu poněkud rozšířené možnosti; to si ale ukážeme až později, až se budeme těmito strukturami speciálně zabývat.

Jedno, nebo druhé!

Pozor na to, abychom nevytvořili dvě instance téhož řídicího objektu, jež se budou prát o stejné postavení – jednu programově, druhou načtením z NIBu. Zkušeným programátorům v prostředí Cocoa se to stát nemůže; začátečníci, kteří nejsou dosud dostatečně zběhlí v práci s NIBy, však tuto chybu dělají poměrně často – zvláště pak tam, kde je řídicí objekt do NIBu, typicky "MainWindow", uložen automaticky na základě projektového vzoru Xcode. Začátečník pak pomocí nějakého copy/paste zároveň pro týž řídicí objekt udělá alloc/initWithNibName:bundle: namísto toho, aby použil jen obsah vhodného "outletu", a neštěstí je hotové.

Pak se stane to, že některé vazby vedou na jeden z řídicích objektů, jiné na druhý; každý objekt také má samozřejmě vlastní rámce (protože oba dva řídicí objekty – jak ten z MainWindow.nib, tak i ten vytvořený pomocí alloc/initWithNibName:bundle: – si načetly vlastní NIB), z nichž ale jen jedna sada je viditelná. Aplikace pak funguje "naprosto divně", stává se, že "do objektu uložíme hodnotu a ona tam není" apod.

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: