Programování pro iOS - 27. Kde vzít rámce - 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 - 27. Kde vzít rámce

2. února 2011, 00.00 | Kde a jak se vezmou rámce, jež spravuje řídicí objekt (instance třídy UIViewController nebo některé z jejích podtříd)? O tom podrobně pojednává dnešní díl.

Rozhodně nejběžnější a v naprosté většině případů také nejšikovnější je načtení rámců z NIBu. Připomeňme to, co jsme si o vzájemném vztahu NIBů a řídicích objektů rámců řekli minule: za běžných okolností má každý řídicí objekt vlastní NIB, obsahující právě ty rámce, jež tento řídicí objekt spravuje. V témže NIBu případně mohou být i další pomocné objekty – třeba i další řídicí objekt rámců, který se zobrazí modálně "přes" tento –, ale obecně platí to, že

• prakticky nikdy není řídicí objekt v témže NIBu jako rámce, jež spravuje;

• prakticky nikdy nejsou v jednom společném NIBu rámce spravované více různými řídicími objekty;

• jen málokdy jsou rámce, spravované jedním řídicím objektem, rozloženy do více NIBů.

U běžných aplikací se tedy nejčastěji setkáme s jednoduchou a přehlednou strukturou, složenou z několika řídicích objektů, z nichž každý má právě jeden NIB obsahující jeho "obrazovku" – tedy všechny rámce, které ji tvoří.

Tyto rámce musí být sestaveny do hierarchické struktury, v níž je jeden kořenový rámec; ten musí (až na nepatrné množství výjimek, jimiž se zatím zabývat nebudeme – s některými z nich se seznámíme později) zabírat plochu celé obrazovky, a musí být neprůhledný. To je důležité; knihovny iOS totiž ve chvíli, kdy je jeden řídicí objekt aktivní a jeho rámce jsou zobrazeny, neudržují aktuální zobrazení uvnitř ostatních rámců – pokud by tedy byly vidět "vedle" nebo "skrz" rámce aktivní, vzhled aplikace by byl nekorektní.

Kořenový rámec je vždy přístupný uvnitř atributu

@property(nonatomic, retain) UIView *view;

řídicího objektu. Pro načítání z NIBu je tedy zapotřebí tento atribut spojit s odpovídajícím rámcem v Interface Builderu. Vzhledem k tomu, že – jak už víme – sám řídicí objekt prakticky nikdy není v témže NIBu jako jeho rámce, bude řídicí objekt vždy reprezentován v NIBu jako "File's Owner". Právě objektu "File's Owner" tedy – je-li to zapotřebí – v inspektoru identity nastavíme odpovídající třídu (typicky nějakou naši vlastní podtřídu UIViewController), a jeho "outlet" view (zděděný právě od třídy UIViewController) "nadrátujeme" na kořenový rámec.

To je vše: řídicí objekt sám načte NIB ve chvíli, kdy to je zapotřebí – v podstatě právě tehdy, když se kdokoli pokusí poprvé získat hodnotu jeho atributu view. Tím vytvoří v paměti celou potřebnou strukturu rámců a zároveň také propojí ten kořenový se svým atributem view.

Zpráva viewDidLoad

Poměrně často je ovšem zapotřebí po načtení rámců z NIBu zajistit ještě dodatečnou inicializaci – např. zobrazení aktuálních hodnot v textových polích apod. Vzhledem k tomu, že řídicí objekt rámce si NIB načítá "podle potřeby kdy se mu zachce", potřebujeme nějakou informaci o tom, kdy přesně byl NIB načten, abychom tuto dodatečnou inicializaci mohli provést.

Třída UIViewController samozřejmě takovou informaci poskytuje: je jí zpráva viewDidLoad, již pošle sama sobě hned po načtení NIBu (nebo vytvoření kořenového rámce jakkoli jinak):

-(void)viewDidLoad;

Pro podtřídy UIViewControlleru je naprosto typické, že reimplementují metodu viewDidLoad, a v jejím rámci provedou všechny potřebné inicializace – mnohdy i ty, jež by v principu bylo stejně dobře možné umístit do metody initWithNibName:bundle:.

Je vhodné si zvyknout vždy poslat touž zprávu nadřízené třídě:

-(void)viewDidLoad {
  [super viewDidLoad];
  ...
}

Ačkoli přímo u samotné třídy UIViewController to není bezpodmínečně nutné, podstatné to je u některých jejích standardních podtříd, např. UITableViewController.

Využití standardního rámce

V některých případech není zapotřebí vůbec sestavovat a načítat NIB, obsahující rámce, protože nám zcela stačí standardní kořenový rámec. Ten – jak víme z minulého dílu – instance třídy UIViewController (nebo díky dědičnosti libovolné její podtřídy) vytvoří automaticky v případě, že je inicializována s hodnotou nil v argumentu nibName, a že neexistuje NIB téhož jména, jako je jméno třídy.

Nejčastěji toho využíváme u již zmíněné třídy UITableViewController a jejích podtříd, jež vytvářejí kořenový rámec UITableView; někdy se to ale může hodit i u "jednoduché" třídy UIViewController, vytvářející obyčejné UIView. Typickým případem může být třeba modální obrazovka, jež má být překryta pouze jednobarevným pozadím, na němž je umístěn pouze jediný programově generovaný prvek grafického uživatelského rozhraní (nebo několik málo takových).

Pak prostě sestavíme odpovídající třídu, aniž bychom vytvářeli adekvátní NIB; GUI programově sestavíme v metodě viewDidLoad, kde již máme standardně připravené view k dispozici:

-(void)viewDidLoad {
  [super viewDidLoad];
  self.view.backgroundColor=[UIColor blueColor];
  UIView *v=... dynamicky vytvořený prvek ...
  [self.view addSubview:v];
}

Programové vytvoření rámců

Co ale v případě, kdy bychom chtěli programově vytvořit nejen obsah kořenového rámce, ale i tento rámec samotný?

Třída UIViewController nabízí adekvátní API i pro tento případ. Celá výše popsaná logika "pokusím se načíst NIB buď zadaného jména, nebo – bylo-li zadané jméno nil – jména NSStringFromClass(self.class); pokud se to nepodaří, vytvořím automaticky UIView" je totiž implementována uvnitř veřejné a zdokumentované metody

-(void)loadView;

Tuto metodu ovšem můžeme překrýt vlastní implementací (z níž samozřejmě nebudeme posílat zprávu nadřízené třídě). V ní pak můžeme vytvořit libovolný kořenový rámec (a podle potřeby také libovolnou hierarchii rámců uvnitř něj, samozřejmě), a uložit jej do atributu view, asi nějak takto:

-(void)loadView {
  MyView *v=[[MyView alloc] initWithFrame:
    [[UIScreen mainScreen] applicationFrame]];
  self.view=v;
  [v release];
  ... [self.view addSubview: ... ] ...
}

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: