Programování pro iOS - 19. Další standardní recognizery - 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 - 19. Další standardní recognizery

8. prosince 2010, 00.00 | Minule jsme se seznámili s trojicí standardních tříd UITapGestureRecognizer, UILongPressGestureRecognizer a UIPanGestureRecognizer umožňující implementovat rozpoznávání běžných gest. Dnes jsou na řadě zbývající standardní recognizery.

UISwipeGestureRecognizer

Poslední ze tříd, jež jsme si ukázali v minulém dílu – UIPanGestureRecognizer – interpretovala "tažení". Gesto "swipe" (českým překladem pro toto gesto je prý "švihnutí") je mu hodně podobné provedením, ale zásadně se liší interpretací.

Jde o poměrně krátký a přímý tah, jehož účelem není "změna polohy", nýbrž "předání speciálního příkazu" – mnohdy se můžeme na toto gesto dívat jako na "přeškrtnutí". Typickým využitím v iOS je rušení řádků tabulky.

Vzhledem k tomu, že jde o jednoduchý příkaz, je akce poslána pouze jednou, ve chvíli, kdy je gesto rozpoznáno.

Pro detailní nastavení slouží dvojice atributů

numberOfTouchesRequired – počet prstů, jež jsou pro gesto použity. Standardní hodnota je samozřejmě 1;

direction – směry, v nichž je gesto akceptováno.

Atribut direction může obsahovat libovolnou kombinaci konstant UISwipeGestureRecognizerDirectionRight, UISwipeGestureRecognizerDirectionLeft, UISwipeGestureRecognizerDirectionUp a UISwipeGestureRecognizerDirectionDown; konstanty lze spojovat standardním způsobem pomocí operátoru '|'. Výchozí hodnotou atributu je UISwipeGestureRecognizerDirectionRight; nezměníme-li ji tedy, gesta ve všech ostatních směrech jsou ignorována.

Metoda locationInView: vrací pozici, na niž bylo gesto zahájeno (a jde-li o gesto více prstů, spočte opět střední polohu mezi nimi).

UIPinchGestureRecognizer

V předminulém dílu jsme si ukázali jakous takous implementaci rozpoznávání standardního gesta "pinch zoom" pro změnu měřítka. Viděli jsme, že i přesto, že jsme si dovolili několik zjednodušení a lehkých nekorektností, vůbec nebyla jednoduchá; také jsme si řekli, že implementovat ji poctivě v podstatě nemá smysl.

Příčinou toho, že to nemá smysl, je právě třída UIPinchGestureRecognizer; její pomocí můžeme korektní rozpoznávání tohoto gesta snadno připojit k libovolnému rámci – samozřejmě, od iOS 3.2 nahoru. A co v iOS 3.1 nebo starším? Inu, v současnosti je už pravděpodobnost, že bude zapotřebí podporovat tento poměrně starý systém, docela malá; pokud tomu u některého z našich projektů je, v naprosté většině případů můžeme beztak využít služeb třídy UIScrollView (na niž se ostatně v některém z budoucích dílů podíváme podrobněji, protože od dávno známé třídy NSScrollView se dost liší).UIPinchGestureRecognizer nemá žádné speciální nastavení.

Akce posílá samozřejmě průběžně, dokud uživatel v gestu pokračuje; můžeme při nich zjistit aktuální měřítko a také rychlost jeho změny pomocí atributů

scale – aktuální měřítko: hodnota menší než 1 při zmenšování, větší při zvětšování;

velocity – rychlost změny měřítka (jako faktor "změna scale za sekundu").

Zatímco atribut velocity je určen pouze ke čtení, hodnotu atributu scale můžeme měnit. Je to obecně zapotřebí v téže situaci, kdy jsme u recognizeru UIPanGestureRecognizer použili metodu setTranslation:inView:.

Jednou z typických situací je případ, kdy v rámci našeho vnitřního kódu změníme měřítko zobrazovaných dat; pak je třeba atribut scale "vynulovat" (tedy nastavit na jedničku), aby další změny vycházely korektně:

-(void)viewDidLoad {
  ...
  UIPinchGestureRecognizer *sgr=[[UIPinchGestureRecognizer alloc]
    initWithTarget:self action:@selector(scale:)];
  [self.view addGestureRecognizer:sgr];
  [sgr release];
  ...
}
-(void)scale:(UIPinchGestureRecognizer*)sgr {
  if (sgr.state==UIGestureRecognizerStateChanged) {
    CGFloat newScale=sgr.scale;
    ... interní změna měřítka na newScale ...
    sgr.scale=1;
  }
}

Za chvilku si to ukážeme na konkrétním praktickém příkladu.

UIRotationGestureRecognizer

Poslední ze standardních recognizerů interpretuje také gesto dvou prstů – standardní rotaci. Jeho programátorské rozhraní je velmi podobné tomu, jež nabízí UIPinchGestureRecognizer; jen na rozdíl od jeho atributu scale zde máme atribut rotation. Vše ostatní odpovídá – specifické nastavení žádné, akce posílá průběžně, a nabízí i atribut velocity. Stejně tak můžeme také měnit hodnotu atributu rotation, kdykoli je to zapotřebí.

Kreslení s recognizery

Ukažme si konkrétní kód v našem kreslicím programu, upravený pro použití třídy UIPinchGestureRecognizer (musíme samozřejmě změnit "Base SDK" minimálně na iOS 3.2, dnes tedy nejspíše na aktuální iOS 4.2). V následujícím výpisu je kompletní implementace (v rozhraní v podstatě nemusíme měnit nic; vhodné je odstranit instanční proměnné t1 a t2, jež už nejsou k ničemu zapotřebí; zapomeneme-li však na to, nic se nestane).

Vidíme, že se nám kód velmi zásadně zjednodušil – a to jsme navíc přidali možnost rotace obrázku, která původně k dispozici nebyla!

@implementation ExampleViewController
@synthesize lines,start,current;
-(void)viewDidLoad {
  // jen u prvého recognizeru je třeba nastavení
  UITapGestureRecognizer *tgr=[[[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(removeLastLine)]
    autorelease];
  tgr.numberOfTapsRequired=3;
  [self.view addGestureRecognizer:tgr];
  // u ostatních nic nenastavujeme
  [self.view addGestureRecognizer:
    [[[UILongPressGestureRecognizer alloc]
      initWithTarget:self action:@selector(askToRemoveAllLines:)]
      autorelease]];
  [self.view addGestureRecognizer:
    [[[UIPanGestureRecognizer alloc]
      initWithTarget:self action:@selector(dragged:)]
      autorelease]];
  [self.view addGestureRecognizer:
    [[[UIPinchGestureRecognizer alloc]
      initWithTarget:self action:@selector(scale:)]
      autorelease]];
  [self.view addGestureRecognizer:
    [[[UIRotationGestureRecognizer alloc]
      initWithTarget:self action:@selector(rotate:)]
      autorelease]];
}
-(void)removeLastLine {
  if (lines.count) {
    [lines removeLastObject];
    [self.view setNeedsDisplay];
  }
}
-(void)askToRemoveAllLines:(UILongPressGestureRecognizer*)pgr {
  // pozor na to, že long press posílá akci při každé změně
  if (pgr.state!=UIGestureRecognizerStateBegan) return;
  [[[[UIActionSheet alloc] initWithTitle:nil
    delegate:self
    cancelButtonTitle:@"Cancel"
    destructiveButtonTitle:@"Remove all lines"
    otherButtonTitles:nil] autorelease]
      showInView:self.view.window];
}
-(void)actionSheet:(UIActionSheet*)as
  clickedButtonAtIndex:(NSInteger)bi {
  if (bi==0) {
    [lines removeAllObjects];
    [self.view setNeedsDisplay];
  }
}
-(void)dragged:(UIPanGestureRecognizer*)pgr {
  if (pgr.state==UIGestureRecognizerStateBegan)
    start=[pgr locationInView:self.view];
  else if (pgr.state==UIGestureRecognizerStateChanged) {
    current=[pgr locationInView:self.view];
    [self.view setNeedsDisplay];
  } else if (pgr.state==UIGestureRecognizerStateEnded) {
    if (!lines) lines=[NSMutableArray new];
    [lines addObject:
      [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:start],
        [NSValue valueWithCGPoint:current],nil]];
    current=CGPointZero;
    [self.view setNeedsDisplay];
  }
}
-(void)scale:(UIPinchGestureRecognizer*)sgr {
  if (sgr.state!=UIGestureRecognizerStateChanged) return;
  self.view.transform=
    CGAffineTransformScale(self.view.transform,
      sgr.scale,sgr.scale);
  sgr.scale=1;
}
-(void)rotate:(UIRotationGestureRecognizer*)rgr {
  if (rgr.state!=UIGestureRecognizerStateChanged) return;
  self.view.transform=
    CGAffineTransformRotate(self.view.transform,rgr.rotation);
  rgr.rotation=0;
}
-(void)dealloc {
    [lines release],lines=nil;
    [super dealloc];
}
@end

Využití recognizeru UIPanGestureRecognizer pro kreslení však přináší jednu drobnou nevýhodu – všimnete si jí? Každopádně příště si ji vysvětlíme a také si ukážeme, jak se jí vyhnout.

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: