Programování pro iOS - 17. Podpora standardních gest - 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:

Seriály

Více seriálů



Software

Programování pro iOS - 17. Podpora standardních gest

24. listopadu 2010, 00.00 | Problémy s detekcí gest si naštěstí uvědomil samotný Apple. Od verze iOS 3.2 zahrnul do systému novou třídu UIGestureRecognizer: umožňující pohodlnou implementaci detekce gest nezávisle na rámci. Systém navíc standardně nabízí hotové rekognizery pro všechna standardní gesta.

V několika minulých dílech jsme si ukázali standardní podporu interpretace dotekového ovládání v iOSu, realizovaného prostřednictvím zpráv touches...:withEvent: na úrovni rámců nebo jejich řídicích objektů. Viděli jsme také, že už implementace poměrně jednoduchých gest – jako je třeba vícenásobné klepnutí – je poměrně komplikovaná. Složitá gesta více prsty se tak detekují velmi špatně.

Proto jsme si také v minulém dílu ukázali jen orientační implementaci gesta pro zoom: přímo toto gesto nebude zapotřebí implementovat už asi nikdy – iOS 3.1 je na samém konci životnosti (a dokonce i v něm většinu případů, kde bylo třeba detekovat gesto pro změny měřítka, pokryla standardní třída UIScrollView). Budeme-li tedy interpretovat složitá gesta více prstů, nebude to standardní "pinch zoom", ale speciální ovládání, výlučné pro tu kterou konkrétní aplikaci.

Rozhraní třídy UIGestureRecognizer

Pokud chceme služby tříd UIGestureRecognizer jen používat (a není třeba, abychom implementovali vlastní), je její rozhraní velmi jednoduché a pohodlné; v podstatě stačí jen

• instanci vhodné třídy vytvořit;

• případně nastavit atributy požadovaného gesta – je-li to v konkrétním případě zapotřebí;

• a připojit ji k rámci, v němž má gesta interpretovat.

Základní metody

K tomu slouží dvě standardní metody

-initWithTarget:(id)target action:(SEL)action;

Standardní (a také designovaný) inicializátor třídy UIGestureRecognizer. Jeho prostřednictvím nejen vytvoříme požadovanou instanci, ale také specifikujeme objekt, který má být informován o tom, že uživatel provedl odpovídající gesto – obvykle půjde o řídicí objekt rámce –, a zprávu, již recognizer tomuto objektu pošle.

V závislosti na tom, o jaké gesto se jedná, je možné, že zprávu objekt dostane pouze jednou (např. při klepnutí), nebo že ji dostane v průběhu gesta pokaždé, když se jeho atributy změní (např. při tažení).

-(void)addGestureRecognizer:(UIGestureRecognizer*)gr;

Druhou z metod nenalezneme ve třídě UIGestureRecognizer, ale v samotném rámci – tedy ve třídě UIView; nedělá nic jiného, než že k rámci recognizer připojí. Knihovní kód se pak již automaticky postará o to, aby všechny připojené recognizery měly možnost zpracovat události, spojené s gesty nad daným rámcem (v podstatě nejde o nic jiného, než že okno přesměruje zprávy touches...withEvent: recognizerům, jež jsou s rámcem spojeny).

Konkrétní příklady

Ukažme si pro lepší ilustraci několik příkladů; pro jednoduchost budeme předpokládat, že níže uvedený kód je vždy součástí řídicího objektu rámce (tedy vhodného dědice třídy UIViewController).

Prvý z příkladů už nemůže být jednodušší – zajistí, že po klepnutí do rámce bude vždy zavolána jeho metoda tapped:

-(void)viewDidLoad {
  ...
  UIGestureRecognizer *tgr=[[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(tapped)];
  [self.view addGestureRecognizer:tgr];
  [tgr release];
  ...
}
-(void)tapped {
  ...
}

Druhý příklad ukazuje i jednoduché konkrétní nastavení; opět jako příklad použijeme standardní třídu UITapGestureRecognizer (kompletní seznam standardních recognizerů a možností, které nám nabízejí, si ukážeme později):

-(void)viewDidLoad {
  ...
  UITapGestureRecognizer *dtgr=[[UITapGestureRecognizer alloc]
    initWithTarget:self     action:@selector(tappedTwiceWithRecognizer:)];
  [self.view addGestureRecognizer:dtgr];
  dtgr.numberOfTapsRequired=2;
  [dtgr release];
  ...
}
-(void)tappedTwiceWithRecognizer:(UITapGestureRecognizer*)dtgr {
  ...
}

Povšimněme si, že tentokrát bylo zapotřebí deklarovat proměnnou, obsahující odkaz na recognizer, jako instanci konkrétní třídy UITapGestureRecognizer – to proto, abychom měli k dispozici její atribut numberOfTapsRequired. Také zde vidíme, že akce může (ale nemusí, jak ukázal prvý příklad) mít jeden argument; je-li tomu tak, je jím právě recognizer, který ji vyvolal.

Další služby

Samozřejmě, že k jednomu rámci můžeme připojit libovolně mnoho recognizerů – kupříkladu oba dva z výše uvedených příkladů. Pak ovšem narazíme na problém, který jsme poznali už dříve, když jsme klepnutí a vícenásobné klepnutí interpretovali přímo: pokud bychom chtěli, aby dvojí klepnutí nebylo nejprve interpretováno jako jednoduché (tedy abychom v našem konkrétním případě nedostali zprávu tapped vždy před zprávou tappedTwiceWithRecognizer:), je třeba se o to postarat speciálním způsobem.

Recognizery samy nabízejí službu, která umožní takovéto závislosti deklarovat: je jí zpráva requireGestureRecognizerToFail:, kterou si vyžádáme, aby jeden recognizer "fungoval" pouze v případě, že jiný gesto nerozpozná. Můžeme tedy napsat např. takovýto kód:

-(void)viewDidLoad {
  ...
  UIGestureRecognizer *tgr=[[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(tapped)];
  [self.view addGestureRecognizer:tgr];
  [tgr release];
  UITapGestureRecognizer *dtgr=[[UITapGestureRecognizer alloc]
    initWithTarget:self     action:@selector(tappedTwiceWithRecognizer:)];
  [self.view addGestureRecognizer:dtgr];
  dtgr.numberOfTapsRequired=2;
  [dtgr release];
  [tgr requireGestureRecognizerToFail:dtgr]; // *
  ...
}

Pomocí řádku označeného hvězdičkou zajistíme, že recognizer tgr (který reaguje na jednoduché klepnutí a dojde-li k němu, pošle zprávu tapped) neudělá nic, dokud recognizer dtgr (který reaguje na dvojí klepnutí) nezjistí, že gesto neodpovídá. Jinými slovy – dvojí klepnutí pošle zprávu tappedTwiceWithRecognizer:, ale nikoli zprávu tapped.

Samozřejmě, že – přesně stejně, jako když jsme interpretovali gesta přímo – před posláním zprávy tapped uplyne nějaká chvilka, v níž musí systém počkat, zda druhé klepnutí nastane nebo ne. Tomu se nelze nijak vyhnout.

Přístup k recognizeru uvnitř posílané zprávy umožňuje zjistit pozici uvnitř rámce, na níž gesto nastalo (nebo na níž právě probíhá u složitějších gest):

-(void)tappedTwiceWithRecognizer:(UITapGestureRecognizer*)dtgr {
  NSLog(@"Klepnuto na pozici %@",NSStringFromCGPoint(
    [dtgr locationInView:self.view]
    )];
}

Recognizery toho nabízejí ještě více: můžeme např. pomocí zprávy addTarget:action: deklarovat více různých příjemců více různých zpráv při rozpoznání gesta, nebo můžeme využít atributu delegate spolu s objektem, odpovídajícím protokolu UIGestureRecognizerDelegate, pro detailní sledování běhu recognizeru a také pro řízení jeho aktivity a souběhu s oststními recognizery, ještě jemnější a flexibilnější, než prostá deklarace requireGestureRecognizerToFail:, popsaná výše. To je ale v praxi zapotřebí poměrně výjimečně, takže detailně si to popisovat nebudeme.

Namísto toho se příště podíváme na seznam konkrétních hotových recognizerů, jež nám iOS nabízí.

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  

 

 

 

Nejčtenější články
Nejlépe hodnocené články
Apple kurzy

 

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

Uživatelské jméno:

Heslo: