Programování pro iOS - 42. UISplitViewController - 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 - 42. UISplitViewController

18. května 2011, 00.00 | Dříve, než se pustíme do podrobného výkladu služeb, možností, výhod a také nedostatků a nedomyšleností nového vývojového prostředí Xcode 4, dokočíme naši současnou sekci věnovanou řídicím objektům rámců. Moc nám toho zde již nezbývá.

Moc nám toho zde již nezbývá, pokud si dovolíme velmi specializované případy (třeba takový UIImagePickerController nebo UIVideoEditorController) přeskočit. Dnes se podíváme blíž na UISplitViewController. Základní princip funkce třídy UISplitViewController je velmi jednoduchý a z uživatelského hlediska jej důvěrně známe z řady standardních aplikací – využívá jej kupříkladu Mobile Mail.

Podobně jako ostatní řídicí objekty rámců, jimiž jsme se zabývali v poslední době, je i UISplitViewController "kontejnerem", který obsahuje jiné řídicí objekty a stará se o jejich zobrazování. Ačkoli API, jež nabízí přístup k těmto vnořených řídicím objektům je nám již dávno známé obecné pole

@property(nonatomic, copy) NSArray *viewControllers;

tentokrát jsou možnosti pro jeho obsah přísně omezeny: musí vždy obsahovat právě dva řídicí objekty rámců. Prvý z nich by měl vždy obsahovat nějaký "obsah" – v praxi je jím téměř vždy tabulka, jež zobrazuje možné volby v nějakém seznamu. Druhý z nich pak je tímto obsahem řízen, a standardně zobrazuje detailní informace o volbě, vybrané v tom prvém.

Třída UISplitViewController se pak stará o správné zobrazení obou řídicích objektů:

• je-li iPad – stejně jako "popovery", i "split view" je určeno výhradně pro něj – v horisontální poloze, zobrazí se rámce obou řídicích objektů vedle sebe. Ten prvý v poněkud užším sloupci, ten druhý pak zabere celý zbytek obrazovky;

• je-li naproti tomu iPad ve vertikální poloze, rámce prvého řídicího objektu jsou skryty, a vidět je pouze ten druhý. Předpokládá se, že pro dočasné zobrazení toho prvého a "výběr z obsahu" bude použit popover (sám aktivovaný stisknutím tlačítka v příkazové liště) – a třída UISplitViewController k tomu nabízí poměrně pohodlnou podporu.

Naopak pro komunikaci mezi hlavním "obsahovým" a podřízeným "detailním" řídicím objektem nenabízí podporu žádnou; to je čistě věcí programátora. Velmi často se k tomu využívá mechanismu delegace; my si zde ukážeme alternativní, poněkud flexibilnější přístup, založený na notifikacích.

Pojďme si přidat "split view" do naší aplikace!

Jelikož v minulých dílech našeho seriálu jsme umístili popovery do "SecondViewControlleru", použijeme nyní pro praktickou ukázku "split view" dosud nedotčený řídicí objekt prvé záložky, FirstViewController. Bude zapotřebí, abychom postupně implementovali následující kroky:

a) sestavíme řídicí objekt pro hlavní "obsah"; použijeme k tomu dávno známý UTableViewController;

b) upravíme FirstViewController tak, aby tomuto obsahu mohl sloužit jako podřízený kontrolér pro zobrazování detailů;

c) postaráme se o komunikaci mezi nimi;

d) zajistíme, aby do "tab baru" – který, jak si jistě vzpomínáte, tvoří základní kostru naší aplikace – byl namísto FirstViewControlleru uložen řídicí objekt třídy UISplitViewController, obsahující oba naše řídicí objekty;

e) a nakonec ještě doplníme podporu pro "toolbar" a tlačítko, otvírající "popover" s obsahem je-li zařízení postaveno na výšku.

Jak hned uvidíme, není to nijak obtížné; s výjimkou bodu (d), v němž se dopustíme určité "prasárny", abychom si trochu usnadnili práci, proto tentokrát věnujeme nějaký čas i tomu, abychom programovali čistě a korektně :)

Řídicí objekt tabulky

Tohle už je pro nás určitě dávno "stará vesta": následující kód můžeme uložit rovnou na začátek zdrojového souboru "FirstViewController.m", nebo – chceme-li – do samostatných zdrojových souborů (pak samozřejmě bude zapotřebí ten s rozhraním do souboru "FirstViewController.m" importovat).

Obsahuje nejjednodušší možnou tabulku s fixním obsahem a s odesláním notifikace po zvolení některého z řádků. Navíc je zde pouze ještě metoda shouldAutorotateToInterfaceOrientation:, jež zajistí, aby bylo možno tabulku zobrazit v kterékoli orientaci zařízení:

@interface FixedTableVC:UITableViewController {
  NSArray *items;
}
@end
@implementation FixedTableVC
-(NSInteger)tableView:(UITableView*)tv
  numberOfRowsInSection:(NSInteger)section {
  if (!items) items=[[NSArray alloc] initWithObjects:
    @".223 Remington",
    @".380 Winchester",
    @".408 CheyTac",
    @".50 BMG",nil];
  return items.count;
}
-(UITableViewCell*)tableView:(UITableView*)tv
  cellForRowAtIndexPath:(NSIndexPath*)ip {
  UITableViewCell *cell=
    [tv dequeueReusableCellWithIdentifier:@"0"];
  if (!cell)
    cell=[[[UITableViewCell alloc]
      initWithStyle:UITableViewCellStyleDefault
      reuseIdentifier:@"0"]
      autorelease];
  cell.textLabel.text=[items objectAtIndex:ip.row];
  return cell;
}
-(void)tableView:(UITableView*)tv
  didSelectRowAtIndexPath:(NSIndexPath*)ip {
  [[NSNotificationCenter defaultCenter]
    postNotificationName:@"Selected"
    object:[items objectAtIndex:ip.row]];
}
-(BOOL)shouldAutorotateToInterfaceOrientation:
  (UIInterfaceOrientation)io {
  return YES;
}
@end

Příjem a zobrazování detailů ve FirstViewControlleru

Podíváme-li se do souboru "FirstView.xib", který nám připravil projektový vzor Xcode, uvidíme, že zde je textové pole UIlabel a dokonce při horním okraji rámce i "toolbar". Ty budeme moci oba přímo využít.

Začneme tedy tím, že si připravíme "outlety" pro přístup k nim (v tomto kroku budeme potřebovat pouze "label"; při jednom si ale připravíme odkaz na "toolbar", který využijeme až nakonec).

Slíbili jsme si, že dnes budeme psát poctivě a nebudeme ignorovat nebezpečí "memory leaků"; namísto prostých instančních proměnných, jež by, jak víme, objekty GUI automaticky "retainovali", si připravíme "outlety" jako atributy typu assign – to zajistí, že při jejich vyplnění nebude retain použit. Bylo by to samozřejmě zbytečné: objekty GUI drží při životě sám kořenový rámec (a ten je automaticky "retainován" ve zděděném atributu view):

@interface FirstViewController:UIViewController
@property (assign,nonatomic) IBOutlet UILabel *label;
@property (assign,nonatomic) IBOutlet UIToolbar *toolbar;
@end

Pak otevřeme XIB a "nataháme dráty" pro správné propojení objektů GUI s nově vytvořenými "outlety" – to už si dnes snad opravdu obrázkem ilustrovat nemusíme :)

V implementaci pak přidáme do vhodné standardní metody – dobře se na to hodí viewDidLoad – registraci pro příjem notifikace, kterou tabulka prvého řídicího objektu posílá, a hned použijeme "outlet" label pro zobrazení jejího obsahu:

-(void)viewDidLoad {
  [super viewDidLoad];
  [[NSNotificationCenter defaultCenter]
    addObserver:self
    selector:@selector(didSelect:)
    name:@"Selected" object:nil];
}
-(void)didSelect:(NSNotification*)nn {
    label.text=nn.object;
}

Je ovšem zapotřebí ještě korektně vyprázdnit odkazy na objekty GUI v případě, že je hlavní rámec (který je obsahuje) uvolněn; zároveň se v takovém případě můžeme odhlásit od příjmu notifikací:

-(void)viewDidUnload {
    [super viewDidUnload];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    label=nil;
    toolbar=nil;
}

Vnucení UISplitView do "tab baru"

V tomto kroku si dovolíme trochu "podraz" – ušetříme si tím práci. Pokud bychom aplikaci teprve sestavovali, samozřejmě správným řešením by bylo do "tab baru" umístit přímo řídicí objekt třídy UISplitViewController; ten bychom patřičným způsobem inicializovali, a nikde by žádný problém nebyl.

My ovšem již máme hotovou strukturu, kde "tab bar" obsahuje přímo náš "FirstViewController". Abychom si ušetřili práci s jejím předěláváním, prostě dynamicky strukturu aplikace změníme z kódu ve chvíli, kdy se náš řídicí objekt načte – dále tedy upravíme metodu viewDidLoad, aby vypadala takto:

-(void)viewDidLoad {
  [super viewDidLoad];
  UITabBarController *tbc=self.tabBarController;
  NSArray *a=tbc.viewControllers;
  unsigned n=[a indexOfObjectIdenticalTo:self];
  if (n!=NSNotFound) {
    UISplitViewController *svc=
      [[UISplitViewController alloc] init];
    svc.viewControllers=[NSArray arrayWithObjects:
      [[[FixedTableVC alloc] init] autorelease],
      self,
      nil];
    NSMutableArray *ma=[a mutableCopy];
    [ma replaceObjectAtIndex:n withObject:svc];
    [tbc performSelector:@selector(setViewControllers:)
      withObject:ma
      afterDelay:0];
  }
  [[NSNotificationCenter defaultCenter]
    addObserver:self
    selector:@selector(didSelect:)
    name:@"Selected" object:nil];
}

Smysl přidaného kódu je asi zřejmý: pokud zjistíme, že náš řídicí objekt je dosud součástí "tab baru", vytvoříme nový UISplitViewController a také nový řídicí objekt "obsahové" tabulky FixedTableVC. Do "split view" uložíme řídicí objekt tabulky a řídicí objekt FirstViewController. Pak pošleme "tab baru" odloženou zprávu setViewControllers:, v níž mu namísto FirstViewControlleru vnutíme právě nově vytvořený UISplitViewController.

Nyní již můžeme aplikaci vyzkoušet; bude fungovat dobře v té míře, že v horizontálním režimu uvidíme vedle sebe oba řídicí objekty a volba řádku v tabulce zobrazí jeho obsah ve FirstViewControlleru:

Ve vertikální poloze ale zatím zůstává toolbar prázdný; musíme se ještě nějak postarat o vložení a aktivaci odpovídajícího tlačítka. Naopak o vytvoření a otevření "popoveru" se starat nemusíme; to za nás provede knihovní kód.

Podpora toolbaru

API, jež třída UISplitViewController nabízí pro podporu práce s "toolbarem", je pohodlné, ačkoli na první pohled poněkud netypické. Zkušený programátor by patrně očekával, že třída UISplitViewController bude obsahovat metodu pro otevření "popoveru"; není tomu ale tak. Místo toho nám v metodě delegáta přidá rovnou připravené tlačítko, jež obsahuje adekvátní "akci" i "cíl"; my už jen musíme zkonfigurovat jeho vzhled a do "toolbaru" je vložit – využijeme zde "outletu", který jsme si připravili dříve spolu s "labelem":

-(void)splitViewController:(UISplitViewController*)svc
  willHideViewController:(UIViewController*)vc
  withBarButtonItem:(UIBarButtonItem*)bbi
  forPopoverController:(UIPopoverController*)poc {
  bbi.title=@"Select...";
  [toolbar setItems:[NSArray arrayWithObject:bbi] animated:YES];
}

Druhá metoda delegáta nás pak informuje o tom, že je vhodné tlačítko z "toolbaru" opět odstranit:

-(void)splitViewController:(UISplitViewController*)svc
  willShowViewController:(UIViewController*)vc
  invalidatingBarButtonItem:(UIBarButtonItem*)bbi {
  [toolbar setItems:[NSArray array] animated:YES];
}

Nakonec ovšem nesmíme zapomenout na to, abychom náš řídicí objekt nastavili pro "split view" jako delegáta, abychom adekvátní zprávy vůbec dostávali (a v rozhraní uvedeme, že třída FirstViewController odpovídá protokolu UISplitViewControllerDelegate):

-(void)viewDidLoad {
  ...
    [tbc performSelector:@selector(setViewControllers:)
      withObject:ma
      afterDelay:0];
    svc.delegate=self;
  }
  [[NSNotificationCenter defaultCenter]
    addObserver:self
    selector:@selector(didSelect:)
    name:@"Selected" object:nil];
}

– a to je vše, tím jsme hotovi:

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: