Programování pro iOS - 35. Ještě jednou UINavigationController - 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ů



Informace

Programování pro iOS - 35. Ještě jednou UINavigationController

30. března 2011, 00.00 | Posledních pár dílů našeho seriálu se zabýváme možnostmi a službami třídy UINavigationController, která usnadňuje sestavování aplikací, jež pracují s hierarchicky strukturovanými daty. Dnes si ukážeme několik posledních zajímavostí, s nimiž můžeme při jejím používání často přijít do styku.

V tomto dílu našeho seriálu, věnovaného programování ve vývojovém prostředí Cocoa/iOS, dokončíme orientační přehled služeb třídy UINavigationController: ukážeme si možnosti delegáta, zmíníme se o skrývání záhlaví a o přístupu k příkazové liště při dolním okraji obrazovky.

Úpravy tabulky

Nejprve se ale ještě rychle podíváme na drobný restík odminula – tehdy jsme si ukázali, jak prostřednictvím standardního atributu rightBarButtonItem můžeme do záhlaví přidat tlačítko "Edit". Při té příležitosti jsme také viděli, že (a) toto tlačítko nemusíme sami vytvářet, nalezneme je již připravené v atributu editButtonItem našeho řídicího objektu (self), a (b) že v tomto případě – a je-li řídicí objekt dědicem třídy UITableViewController – skoro vše funguje "samo".

Jediné, co musíme doplnit, je adekvátní metoda datového zdroje (minule jsem se omylem zmínil o "delegátu", to bylo špatně – Apple má služby delegáta a datového zdroje rozdělené dost nelogicky; v praxi je to ale jedno: stejně delegátem i datovým zdrojem bývá až na naprosté výjimky týž objekt). Nejde sice přímo o službu navigačního systému (nýbrž o práci s tabulkami obecně); užívá se ale právě v souvislosti s výše zmíněným standardním tlačítkem "Edit" tak často, že neuškodí si ji ukázat hned.

Kdykoli prostřednictvím editačního systému smažeme některý z řádků tabulky, pošle tabulka svému datovému zdroji standardní zprávu tableView:commitEditingStyle:forRowAtIndexPath:. My bychom mohli odpovídající metodu naprogramovat třeba takto:

-(void)tableView:(UITableView*)tv
  commitEditingStyle:(UITableViewCellEditingStyle)es
  forRowAtIndexPath:(NSIndexPath*)ip {
  [(id)self.items removeObjectAtIndex:ip.row];
  [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:ip]
    withRowAnimation:UITableViewRowAnimationFade];
}

musíme si ale dát pozor, abychom v tomto případě řídicí objekt sestavili nad měnitelnými poli (ostatně právě proto, že atribut items máme deklarovaný jako NSArray, je ve výše uvedeném kódu přetypování na id – překladač by jinak ohlásil varování, že NSArray nerozumí zprávě removeObjectAtIndex:). To by mohlo vypadat třeba nějak takto :

SelectionVC *svc=[SelectionVC selectionWithList:
  [NSMutableArray arrayWithObjects:
    @"a",@"b",
    [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4",nil],
    @"c",@"d",
    nil]];

Pak bude vše fungovat zcela korektně, a kterýkoli z řádků budeme moci smazat jak tlačítkem "Edit" tak i – bez jakéhokoli dalšího programování – standardní službou "sweep delete".

Za poznámku snad ještě stojí to, že v rámci datového zdroje tabulky můžeme některé řádky z mazání vyčlenit: implementujeme-li např. v řídicím objektu tabulky tuto metodu:

-(BOOL)tableView:(UITableView*)tv
  canEditRowAtIndexPath:(NSIndexPath*)ip {
    return [[self.items objectAtIndex:ip.row]
      isKindOfClass:[NSString class]];
}

nebude možné mazat řádky, representující hierarchivcky vnořená pole.

Ostatní ovladače pro záhlaví

Vraťme se ale od práce s tabulkou k našemu aktuálnímu tématu, tedy ke službám tříd UINavigationController a UINavigationItem pro navigační systém. Jakkoli je rozhodně skutečně daleko nejběžnější do atributu navigationItem.rightBarButtonItem ukládat právě tlačítko "Edit", můžete tam podle potřeby umístit i jakýkoli jiný ovladač. Ukažme si v rychlosti alespoň dva nejtypičtější příklady.

V prvém z nich do pravého horního rohu obrazovky umístíme některý ze standardních ovladačů iOSu – při příklad zvolíme ovladač "Refresh", a zajistíme, aby jeho stisknutí poslalo našemu řídicímu objektu zprávu reloadData (přesně takto bychom to asi udělali v případě, kdy by se zobrazovaná data načítala z nějakého vzdáleného serveru, a my potřebovali standardní ovladač pro jejich znovunačtení):

self.navigationItem.rightBarButtonItem=[[[UIBarButtonItem alloc]
  initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
  target:self action:@selector(reloadData)]
  autorelease];

Standardních příkazů je dlouhá řada, vedle "Refresh" zde nalezneme řadu dalších častu používaných funkcí: "Done", "Cancel", "Edit", "Save", "Add", "Compose", "Reply", "Organize", "Bookmarks", "Search", "Camera", "Trash", "Play", "Pause", "Rewind", "FastForward", "Undo", "Redo" a obecnou akci "Action", vybavenou poněkud nešťastně zvolenou ikonou. Je vhodné si vždy ověřit aktuální nabídku; firma Apple tyto služby průběžně rozšiřuje (např. v iOSu 4 přibyla akce "PageCurl", již zde asi využijeme jen výjimečně, ale v příkazové liště při dolním okraji obrazovky může být hodně šikovná).

Nenalezneme-li mezi standardními službami požadovanou, můžeme tlačítko vytvořit téměř týmž způsobem s vlastním příkazem; jen namísto initWithBarButtonSystemItem: použijeme initWithImage: nebo initWithTitle: s naší vlastní ikonou nebo nadpisem.

Konečně pak v případě, kdy chceme do pravého horního rohu umístit zcela obecný ovladač, můžeme použít službu initWithCustomView: – kupříkladu takto vpravo nahoře spustíme indikátor aktivity:

UIActivityIndicatorView *ai=[[[UIActivityIndicatorView alloc]
  initWithActivityIndicatorStyle:
    UIActivityIndicatorViewStyleWhite]
  autorelease];
[ai startAnimating];
self.navigationItem.rightBarButtonItem=[[[UIBarButtonItem alloc]
  initWithCustomView:activity]
  autorelease];

Přesně týmž způsobem samozřejmě můžeme ovladače ukládat i doleva (do atributu leftBarButtonItem), pokud nám nevadí, že si tím překryjeme tlačítko "Zpět".

Zajímavým a docela šikovným atributem třídy UINavigationItem je také prompt – uložíme-li do něj nějaký text, zobrazí se drobným písmem v samostatné řádce ještě nad vlastním záhlavím s titulkem.

Delegát

Stejně jako tomu je u většiny ostatních významnějších tříd knihoven Apple, i instance třídy UINavigationController může mít delegáta. Pokud jej nastavíme, bude jej navigátor informovat o probíhajících změnách pomocí zpráv

-(void)navigationController:(UINavigationController*)nc
  willShowViewController:(UIViewController*)vc
  animated:(BOOL)animated;
-(void)navigationController:(UINavigationController*)nc
  didShowViewController:(UIViewController*)vc
  animated:(BOOL)animated;

Zbývající služby navigátoru

Ze zbývajících služeb třídy UINavigationController, na které jsme se dosud nedívali, stojí za zmínku dvě:

@property(nonatomic,getter=isNavigationBarHidden)
  BOOL navigationBarHidden;
-(void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated;

Pomocí atributu navigationBarHidden a s ním spojené služby můžeme celé záhlaví skrýt tam, kde jeho obsah není důležitý a naopak bychom potřebovali více prostoru na obrazovce – v našem případě by tomu tak mohlo být třeba u kořenového objektu, kde tlačítko "Zpět" stejně není zobrazeno. Mohli bychom třeba implementovat takto službu viewWillAppear:-(void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  UINavigationController *nc=self.navigationController;
  nc.navigationBarHidden=
    self==[nc.viewControllers objectAtIndex:0];
}

Konečně pak alespoň za zmínku stojí to, že navigátor podporuje velmi pohodlný přístup k příkazové liště při dolním okraji obrazovky. Především, pomocí velmi podobného atributu jako je navigationBarHidden můžeme řídit, zda bude příkazová lišta zobrazena nebo ne:

@property(nonatomic,getter=isToolbarHidden)
  BOOL toolbarHidden;
-(void)setToolbarHidden:(BOOL)hidden animated:(BOOL)animated;

Je-li příkazová lišta zobrazena, třída UINavigationController při přepínání aktivních objektů automaticky udržuje její obsah tak, aby odpovídal obsahu atributu toolbarItems v právě aktivním řídicím objektu. Tyto "itemy" jsou přesně stejné instance třídy UIBarButtonItem jako položky, jež jsme ukládali výše do atributů leftBarButtonItem a rightBarButtonItem; stejným způsobem je tedy můžeme i vytvářet (navíc zde máme k dispozici standardní položky pro "mezeru" a "flexibilní mezeru", jež v záhlaví neměly smysl).

Dejme tomu, že bychom třeba chtěli v našem hierarchickém prohlížeči umožnit přímý přístup do vnořených polí pomocí příkazové lišty při dolním okraji obrazovky; lišta by se zobrazila pouze pokud je alespoň jedna z položek vnořeným polem – a pro každou takovou by obsahovala jedno tlačítko, jehož stisknutí bude ekvivalentní výběru řádku s tímto vnořeným polem v tabulce.

Zdá se to složité? Ale kdež. Stačí implementovat jen následující tři metody – z nichž ostatně druhá je pouze kopií části stávající služby tableView:willSelectRowAtIndexPath: (konkrétně té, jež se provede při volbě řádku s vnořeným polem), a třetí je jen nepatrně upravenou variantou na automatické skrývání záhlaví, jež jsme si ukázali před chvilkou:

-(NSArray*)toolbarItems {
  NSMutableArray *ma=[NSMutableArray array];
  for (NSArray *ai in self.items)
    if ([ai isKindOfClass:[NSArray class]]) {
      UIBarButtonItem *item=[[[UIBarButtonItem alloc]
        initWithTitle:
          [NSString stringWithFormat:@"%u items",ai.count]
        style:UIBarButtonItemStyleBordered
        target:self action:@selector(pushItem:)]
        autorelease];
      item.tag=(NSInteger)ai;
      [ma addObject:item];
    }
  return ma;
}
-(void)pushItem:(UIBarButtonItem*)sender {
  SelectionVC *svc=[[[SelectionVC alloc]
    initWithItems:(id)sender.tag] autorelease];
  [self.navigationController pushViewController:svc animated:YES];
}
-(void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  UINavigationController *nc=self.navigationController;
  nc.toolbarHidden=self.toolbarItems.count==0;
}

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: