Programování pro iOS - 12. Touching! - 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 - 12. Touching!

20. října 2010, 00.00 | Systém iOS podporuje různé eventy. V praxi je ale nejpodstatnější správa dotykové obrazovky, na kterou se podíváme dnes a v několika dalších pokračováních.

Pardon za ten nadpis, ale jako obvykle jsem si nedokázal odpustit kalambúr. Pravda je, že dostat úspěšně aplikaci na iPhone a posléze pak nay AppStore může programátora, zvyklého na poněkud normálnější distribuční postupy, nemálo dojmout. Ale s tím jsme skončili minule a dnes se pustíme do událostí.

Základní princip

Události od dotykové obrazovky jsou v principu předávány velmi podobným způsobem, jako události myši v Mac OS X, jež jsme si popsali už dávno. Třída NSResponder je součástí UIKitu, jen se jmenuje UIResponder a je poněkud jednodušší; stejně tak je jeho součástí třída UIEvent, která se v ničem zásadním neliší od NSEventu z Mac OS X. Stejně tak i v iOS platí to, že – abych jen mírně modifikoval to, co jsme si řekli tehdy – se nemusíme takřka o nic starat: aplikace se nějak dozví od správce dotekové obrazovky, kde a jak k ní uživatel přiložil prsty; poté nějak zjistí, nad kterým rámcem (UIView) to bylo – a pošle mu standardní zprávu.

Tato zpráva se ale od mouseDown: apod poněkud liší; to proto, že dotykové ovládání s podporou více prstů je přece jen o hodně složitější než myš. Než si ukážeme konkrétní zprávy, jejichž prostřednictvím nám UIKit informace o dotekové obrazovce předává, stojí za to si uvědomit, na jaké základní prvky lze libovolné gesto rozložit:

• kdykoli se prst dotkne obrazovky, je to důležitý moment – začátek jeho tahu. Jelikož prstů lze použít více, lze takto také začít několik na sobě nezávislých tahů;

• kdykoli se prst pohne, jeho tah pokračuje;

• když je prst zvednut, jeho tah je ukončen.

Pořadí událostí tedy může být třeba takovéto:

1. začal tah A na souřadnicích 10,10;

2. začal tah B na souřadnicích 30,10;

3. tah A se přesunul na souřadnice 10,20 a zároveň se tah B přesunul na souřadnice 30,20;

4. začal tah C na souřadnicích 50,20;

5. skončil tah A a zároveň se tah B přesunul na souřadnice 30,30 a tah C na souřadnice 50,30;

6. skončily tahy B a C.

Této logice také přesně odpovídají standardní zprávy, jejichž prostřednictvím nás UIKit o aktivitě uživatele informuje, a jež si ukážeme za malou chvilku. Nejprve se ale musíme seznámit s novou třídou.

Třída UITouch

Zatímco události klávesnice nebo myši, s nimiž jsme se setkávali v Mac OS X, byly natolik jednoduché, že pro jejich vyjádření bohatě stačilo několik datových polí v rámci třídy NSEvent, s tahy to tak jednoduché není: samy o sobě mohou být poměrně složité, a ještě k tomu jich může v jeden okamžik zároveň probíhat několik.

UIKit proto přichází se specifickou třídou, jejíž instance reprezentují jednotlivé tahy – s třídou UITouch.

Kdykoli uživatel zahájí nový tah, systém pro něj založí novou instanci třídy UITouch (a uloží ji do seznamu tahů, který je k dispozici prostřednictvím události, tedy uvnitř aktuální instance UIEvent).

Když uživatel mění souřadnice tahu, odpovídající instance UITouch tyto změny reflektuje.

Po ukončení tahu, tedy když uživatel prst zvedne, je teprve odpovídající instance UITouch zrušena.

Instance třídy UITouch mají několik atributů, z nichž nejpodstatnější a běžně užívané jsou dva:

-(UIView*)view;

Rámec, jemuž tah patří – ten, ve kterém byl zahájen. Je vhodné si uvědomit, že aktuálně po řadě změn souřadnic může tah již dávno probíhat nad docela jiným rámcem; ten původní je ale v tomto atributu stále k dispozici.

Mimochodem, atribut je definován jako property – my se prozatím pro jednoduchost přidržíme "tradiční" terminologie zpráv. Ze všech praktických hledisek je to jedno; v programu můžeme stejně dobře psát "[someTouch view]" i "someTouch.view", ačkoli druhá varianta je v ObjC 2 možná mírně preferovaná.

-(CGPoint)locationInView:(UIView*)view;

CGPoint je v podstatě starý dobrý NSPoint, prostě kombinace souřadnic x a y do jedné společné struktury. Na rozdíly mezi strukturami NS..., jež důvěrně známe z Mac OS X, a variantami CG..., jež důsledně používá iOS (v Mac OS X mimochodem existují také, ale běžně tam s nimi nepracujeme, pokud nepoužíváme některá specifická API) se podíváme až jindy – prozatím stačí, řekneme-li si, že jsou naprosto zanedbatelné.

Zpráva locationInView: pak spočte aktuální polohu tahu v tom rámci, který zadáme jako argument: to může být v podstatě kterýkoli z rámců okna, záleží na tom, v pohledu kterého z rámců nás poloha zajímá. Pokud zpracování událostí implementujeme uvnitř vlastní podtřídy UIView – a tím za chvilku také skutečně začneme –, obvykle zde zadáme prostě a jednoduše self.

Standardní zprávy

Základem mechanismu dotekového ovládání je čtveřice zpráv: prostřednictvím jedné se hlásí započaté tahy, druhá slouží pro informace o změnách, a třetí o ukončení tahů.

Čtvrtá má speciální postavení: hlásí také ukončení tahů, ale ne proto, že uživatel zvedl prsty z obrazovky, nýbrž proto, že celá akce byla přerušena nějakou vnější událostí – třeba zrovna ve chvíli, kdy uživatel prováděl na obrazovce nějakou akci, začal zvonit telefon...

Začneme ale od začátku:

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;

Kdykoli jeden nebo více prstů zahájí tah, pošle systém rámci, nad nímž se tak stalo, zprávu touchesBegan:withEvent:.

Vidíme, že kromě očekávaného argumentu třídy UIEvent má zpráva argument ještě jeden: je to množina, v níž jsou pro pohodlí programátora uloženy právě jen ty tahy, které začaly. Obvykle takový bude jediný; pokud ale uživatel např. klepl na obrazovku dvěma prsty zároveň, může jich být více – pokud se oběma prsty strefil nad týž rámec. Naopak samozřejmě, pokud uživatel klepne na obrazovku dvěma prsty tak, že každý z doteků bude nad jiným rámcem, dostane každý z obou rámců vlastní zprávu touchesBegan:withEvent:, v jejímž argumentu touches bude pouze jediný objekt: právě ten, který danému rámci "patří".

Naopak uvnitř instance UIEvent jsou uloženy všechny aktuální tahy – tedy také ty, které již nějakou dobu probíhají, nebo ty nad ostatními rámci. Můžeme si je od ní kdykoli vyžádat pomocí zprávy allTouches; jen málokdy je to ale v praxi zapotřebí.

-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event;

Zpráva touchesMoved:withEvent: je zaslána kdykoli se změní souřadnice jednoho či více tahů – tedy kdykoli se jeden či více prstů na obrazovce pohnulo. Opět je jejím argumentem množina právě jen těch tahů, jejichž souřadnice se změnily, zatímco seznam všech aktuálních tahů je uložen v objektu event.

Tuto zprávu dostane vždy týž rámec, nad nímž tah začal. I v případě, kdy uživatel přetáhne prst nad odlišný rámec, je příjemcem zpráv touchesMoved:withEvent: stále ten původní.

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event;

Zde již je asi zřejmé, že jakmile uživatel zvedne z obrazovky prst či více, pošle systém zprávu touchesEnded:withEvent:, jejíž první argument obsahuje právě ty tahy, které byly ukončeny. Příjemcem této zprávy je opět ten rámec, nad nímž tyto tahy původně začaly.

Opět je vhodné si uvědomit, že pokud uživatel na počátku položil prsty na více různých rámců, dostane tedy při jejich současném zvednutí tuto zprávu několik rámců zároveň (resp. jeden krátce po druhém) – každý se "svým" tahem v argumentu touches.

-(void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event;

Význam poslední zprávy je asi také jasný: došlo-li z libovolného důvodu k násilnému přerušení gesta, dostane každý rámec, nad nímž alespoň jeden z právě probíhajících tahů začal, tuto zprávu – přičemž v jejím argumentu touches nalezne právě ty "své" tahy.

Možnost omezit gesta

Je asi zřejmé, že zpracování tahů není úplně jednoduché, zvláště pak v případě, kdy jich probíhá více naráz. Standardně jsou proto gesta více prsty v rámcích UIKitu zakázána; systém prostě bere v úvahu pouze první tah a ostatní ignoruje.

Chceme-li pracovat s tahy více prstů bez omezení, musíme nastavit atribut multipleTouchEnabled rámce, buď pomocí odpovídajícího přepínače v Interface Builderu, nebo programově nějak takto:

someView.multipleTouchEnabled=YES;

Je vhodné to mít na paměti; sám jsem před časem strávil dobře hodinu laděním kódu, který zpracovával složitá gesta více prsty, a vůbec nechtěl rozumně fungovat... jako kdyby se některé z tahů ignorovaly. Také že ano: zapomněl jsem nastavit atribut multipleTouchEnabled, a mé rámce dostávaly zprávy pouze o prvém ze zároveň probíhajících tahů!

U rámců, jež vůbec nemají zpracovávat gesta, je možné nastavit atribut userInteractionEnabled na hodnotu NO. U našich vlastních rámců to mívá smysl jen málokdy; velmi praktický je ale tento atribut u složitých systémových rámců, jejichž zpracování gest chceme z nějakého důvodu vypnout.

Kromě toho UIKit nabízí ještě možnost omezit interpretaci tahů jen na jeden rámec – nastavíme-li jeho atribut exclusiveTouch, ostatní rámce téhož okna nedostanou nic. Toho se ale v praxi využívá jen celkem výjimečně.

Pro začátek to stačí...

Jak uvidíme v příštích dílech našeho seriálu, podpora dotekového ovládání nabízí o dost více; teď ale na chvilku ponecháme stranou "suchou teorii", a hned příště si ukážeme zcela konkrétní projekt a praktické použití výše popsaných služeb.

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: