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:
Začínáme s
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):
- Nastal čas na kakao...
- Tak nejdřív kakao ochutnáme...
- Programovací jazyk C: velmi, velmi stručně
- Objective C: to si vysvětlíme podrobněji
- Co jsme si o Objective C ještě neřekli...
- Nastal čas na kakao - Vznik a zánik objektů
- Nastal čas na kakao - Kopírování objektů
- Nastal čas na kakao - Skryté podtřídy
- Nastal čas na kakao - Základní služby objektů
- Nastal čas na kakao - Jak správně psát v Objective C
- Nastal čas na kakao - Jak správně importovat
- Nastal čas na kakao - Podtřídy, delegáti, vkládání, jak se to rýmuje?
- Nastal čas na kakao - Využití kategorií namísto dědičnosti
- Nastal čas na kakao - Vkládání objektů a přesměrování zpráv
- Nastal čas na kakao - Inicializace a rušení objektů
- Nastal čas na kakao - Metody initWith... a designovaný inicializátor
- Nastal čas na kakao - Inicializace: tipy a triky
- Nastal čas na kakao - Accesory: přístup k proměnným instancí
- Nastal čas na kakao - Šedá je teorie, zelený je strom života...
- Nastal čas na kakao - Více o XCode: inspektory
- Nastal čas na kakao - Aplikace RSS2: datový model
- Nastal čas na kakao - Aplikace RSS: implementace datového modelu
- Nastal čas na kakao - Aplikace RSS: parsování XML
- Nastal čas na kakao - Interface Builder a uživatelské rozhraní
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Druhý kontrolér a dokončení aplikace
- Nastal čas na kakao - Drobná vylepšení a zdokonalení...
- Nastal čas na kakao - Ladění
- Nastal čas na kakao - Třídy Foundation Kitu
- Nastal čas na kakao - Třídy Foundation Kitu (2)
- Nastal čas na kakao - Textové řetězce: NS(Mutable)String
- Nastal čas na kakao - Čísla, binární data a další...
- Nastal čas na kakao - Archivace objektů
- Nastal čas na kakao - Trocha magie, aneb distribuované objekty
- Nastal čas na kakao - Málem bychom zapomněli: NSAutoreleasePool
- Nastal čas na kakao - Zpracování výjimek: NSException
- Nastal čas na kakao - NSInvocation a černá magie
- Nastal čas na kakao - Kakao v Tygrovi
- Nastal čas na kakao - Notifikace: nepřímé předávání zpráv
- Nastal čas na kakao - NSUserDefaults
- Nastal čas na kakao - Co nového ve Foundation Kitu
- Nastal čas na kakao – s Intelem, s Intelem, jedeme do...
- Co nového v Xcode
- Začínáme s AppKitem
- Jak MVC v Kakau vypadá doopravdy?
- Jak MVC v Kakau vypadá doopravdy: dokončení
- Přehled tříd AppKitu
- Nastal čas na kakao - Přehled tříd AppKitu 2
- Přehled tříd AppKitu 3: zbývající třídy GUI
- Přehled tříd AppKitu 4: textový systém
- Nastal čas na kakao - Přehled tříd AppKitu 5: hlavně grafika
- Přehled tříd AppKitu 6: dokumentový systém
- Přehled tříd AppKitu 7: dokončení
- Pojmenované vlastnosti objektů
- Pojmenované vlastnosti objektů: implementace
- Pojmenované vlastnosti objektů: relace 1:N
- Pojmenované vlastnosti objektů: řazení jmen a agregační funkce
- Sledování změn objektů
- Sledování změn objektů – ukázka
- Sledování změn objektů – zdrojový kód
- Sledování změn objektů: kód modelu
- Sledování změn objektů: přímý přístup
- Kontroléry a vazby
- Vázání vazeb
- Další vazby s jednoduchým kontrolérem
- Implementace a použití převodu hodnot
- Validace hodnot
- Validace a chyby, a jedna hezká vazba...
- Práce s polem objektů
- Základní vazby NSArrayControlleru
- Převodníky, přepínače, placeholdery
- Mírná vylepšení v mezích zákona
- Objective C 2.0 - novinky z Leoparda
- NSTreeController
- Programování v Cocoa - Pár tipů a triků
- Programování v Cocoa - Základy kreslení
- Kterak nakreslit modrý obdélník...
- Další služby pro kreslení
- Obrázky a písmenka...
- Události a myš
- Lepší práce s myší
- Události klávesnice
- Input Management
- Příkazy a schránka
- Další události
- Táhni a padni
- Byli jsme na tahu; nyní padneme.
- Zvolme si, jak vhodit
- Drobnosti a chybičky
- Speciální případy tahání či házení
- Kterak táhnout něco, co neexistuje?
- Jak na sítě...
- NSURLConnection
- Safari za minutu
- Služby WebKitu
- Kakao v Leopardu
- Druhé Objective C
- Druhé Objective C: různé drobnosti
- Druhé Objective C: kategorie a protokoly
- Druhé Objective C: nový příkaz cyklu
- Druhé Objective C: atributy a accesory
- Druhé Objective C: atributy a accesory
- 64 je dvakrát 32
- Ubicumque dulce est, ibi et acidum invenies...
- Irbis: že prý žádné novinky?
- Blok sem, blok tam, nám už je to všechno jasné...
- Bloky jsou i v AppKitu
- Irbis a Foundation Kit
- Kde jsou má data?
- Kde jsou má data? V NSCache!
- Soubor, jméno, URL, jak se to rýmuje...
- Další podpora NSURL
- Zabíjení!
- A máme tady i...OS!
- Systémové prvky GUI
- Programování pro iOS 1. díl - Rozdíly mezi "i" a "Mac"
- Programování pro iOS - 2. Začínáme programovat
- Programování pro iOS - 3. základní ovladače a propojení GUI s kódem
- Programování pro iOS - 4. Varovná hlášení
- Programování pro iOS - 5. Rámce a jejich řídicí objekty
- Programování pro iOS - 6. Ukládání dat
- Programování pro iOS - 7. Správa paměti a starý restík
- Programování pro iOS - 8. Dokončení aplikace
- Programování pro iOS - 9. Jak dostat aplikaci do iPhone
- Programování pro iOS - 10. Instalace aplikace do cizího iPhone
- Programování pro iOS - 11. Jak dostat aplikaci do libovolného iPhone
- Programování pro iOS - 12. Touching!
- Programování pro iOS - 13. Kreslíme na iPhone
- Programování pro iOS - 14. Udělejme gesto
- Programování pro iOS - 15. Další gesta
- Programování pro iOS - 16. Více prstů, více zábavy
- Programování pro iOS - 17. Podpora standardních gest
- Programování pro iOS - 18. Recognizery v iOS
- Programování pro iOS - 19. Další standardní recognizery
- Programování pro iOS - 20. Co nového v iOSu
- Programování pro iOS - 21. "Multitasking"
- Programování pro iOS - 22. Nulla est honesta avaritia nisi temporis
- Programování pro iOS - 23. Jak se aktivovat, jsme-li v pozadí
- Programování pro iOS - 24. Zbývající drobnosti
- Programování pro iOS - 25. Řídicí objekty rámců
- Programování pro iOS - 26. Jak se dělá UIViewController
- Programování pro iOS - 27. Kde vzít rámce
- Programování pro iOS - 28. Základní služby
- Programování pro iOS - 29. Práce s rámci
- Programování pro iOS - 30. Rotace zařízení
- Programování pro iOS - 31. Správa paměti v rámcích
- Programování pro iOS - 32. Řídicí objekt pro tabulky
- Programování pro iOS - 33. Řídicí objekt pro strom
- Programování pro iOS - 33. Více o UINavigationControlleru
- Programování pro iOS - 35. Ještě jednou UINavigationController
- Programování pro iOS - 36. Po navigátoru taby
- Programování pro iOS - 37. Více o UITabBarControlleru
- Programování pro iOS - 38. Dokončení UITabBarControlleru
- Programování pro iOS - 39. UIPopoverController
- Programování pro iOS - 40. Další triky UIPopoverControlleru
- Programování pro iOS - 41. Zbývající služby UIPopoverControlleru
- Programování pro iOS - 42. UISplitViewController
- Programujeme v
iTunesXcode 4 - Programování pro iOS - 44. Předvolby Xcode 4
- Programování pro iOS - 45. Práce v Xcode 4
- Xcode 4: projekt a cíle
- Xcode 4: práce s cíli
- Xcode 4: Build Settings
- Xcode 4: Build Phases
- Xcode4: Build Phases podruhé
- Xcode 4: Co jsou to Build Rules?
- Xcode4: taje editoru
- Xcode4: automatické doplňování v editoru
- XIBy chyby
- Více o XIBech
- Editor XIBů
- Inspektory pro XIBy
- Vazby mezi objekty v XIBech
- Vazby mezi objekty v kódu
- Paletky Xcode pro XIBy
- Xcode 4: levý sloupec
- Xcode 4: okno Organizer
- Xcode 4: okno Organizer, část druhá
- Xcode 4: co je to Workspace?
- Xcode 4: základy schémat
- Xcode 4: akční schémata