POC: touch handling, drawing rectangle – iOS

Na! Akkor kezdjünk neki a FlatCube-nak! :-) A specifikációban azonosítottunk néhány -ot, melyek közül az első kettőt egyből érdemes is összevonni. Az első önmagában olyan egyszerű lenne, hogy talán meg sem érdemel egy külön cikket, bár ha picit foglalkoznánk az alternatív megoldási lehetőségek kutatásával csakhamar kiderülne, hogy egy három részes cikkhez is elegendő anyagot gyűjtöttünk össze. Mindenesetre most, az egyes POC-ok kidolgozásában egyetlen célunk, hogy a teljesség igénye nélkül “csak” valamilyen/akármilyen de használható megoldást találjunk a címben felsorolt problémák kezelésére.

Emlékeztetőként a két terület amiről információt kell gyűjtenünk a következő volt: 1 - Jelenítsünk meg négyszöget a kijelzőn, 2 - Kezeljünk egy és/vagy több ujjas touch eseményt a megjelenített négyszögön belül és kívül.

Specifikáció

Mint ahogy a címlapképen látható, három négyzetet fogunk kirajzolni úgy, hogy mindhárom átfedésben legyen a másik kettővel valamint legyen olyan pont is, ahol mindhárom átfedi egymást. Helyezzünk el továbbá egy kapcsolót is a felületen amivel a touch esemény kiértékelését paraméterezzük majd. Ezek után a touch event-eket a következők szerint kell kezelni:

  • Ha egyujjas érintés történik a piros négyzet területén belül (akár étfedésben álló részeken is), írjunk ki és növeljünk egy számlálót minden érintés után a piros négyzet közepén.
  • Ha kétujjas érintés történik és az egyik ujj az egyik sárga négyzetben míg a másik a másikban van, akkor az első esethez hasonlóan jelenítsünk meg egy számlálót a sárga négyzetekben is.
  • Ha a kapcsoló ON állapotba kerül, akkor már csak az számít, hogy akár egy, akár két ujjal is, de milyen területet érintettünk meg a felületen, azaz attól már nem függ az eredmény, hogy egy vagy két ujjas érintést alkalmaztunk-e. Ha például a hármas átfedésben lévő területen érintjük meg a felületet, akkor mindkét számlálót növelni kell. Ha két ujjal érintettük meg a piros négyzetet akkor is növelni kell a számlálót, vagy ha olyan két ujjas érintést csinálunk amelyiknél az egyik ujjunk kívül esik mindhárom négyzeten, akkor is kiértékeljük az a négyzeteket érintő ujj tekintetében.

UIView

A objektum egyfajta konténere a megjeleníteni kívánt “dolgoknak”. Egy view-ra több megjelenítendő dolgot is el lehet helyezni, valamint a view-kat egy hierarchiába szervezve egymásra lehet helyezni belőlük tetszőleges számú példányt ().

A fenti példában látható, hogy egy Window objektum található legalul, amire aztán tetszőleges számú view kerülhet.

A view objektumok négyzet alakú, tetszőlegesen színezhető, méretezhető és mozgatható objektumai a felületnek, valamint képesek érzékelni és továbbítani a felhasználói felületről érkező eseményeket a hozzájuk tartozó controller objektum felé.

Drawing rectangle

Az előzőekben elmondottak szerint elegendő egy view-t elhelyezni az Interface Builder-ben - válasszuk a view-t az object libray-ból, és dobjuk a felületre – majd állítsuk be a méretét (Size inspector) és a színét (Attribute inspector). Ez utóbbi inspectorban beállíthatjuk az Alpha értéket is, amely az átlátszóságot szabályozza (A példában ezek 0. 6 értékre vannak beállítva. ).

Amennyiben az átlátszóságot programozzuk, egy érdekes mellékhatással találkozunk. Bármit helyezünk egy 1-nél kisebb Alpha értékkel paraméterezett view-ra, úgy az megörökli ezt az átlátszósági tényezőt, azaz ha betűket írunk rá, azok is annyira lesznek átlátszóak amennyire a view az. A példában szereplő piros és sárga view-k egyaránt 0. 6-os Alpha értékkel szerepelnek, így a rajtuk elhelyezett számláló sem látszik élesen Alpha=1 értékkel. Ehhez a számlálókat nem a view-ra kell helyezni, hanem a view fölé a view szülőjére.

Az ábrákon látható a tervezett felület valamint a hozzá tartozó objektumok listája. A piros színű view elnevezése “Single view”, a hozzá tartozó számláló pedig “Label for single view”. Ahogy látható ez a két objektum azonos szinten van, így a számláló az átlátszóságot a Main view-tól örökli. A Single view egyetlen tartalmazása a hozzá tartozó “simple tap” szöveg Label-je, ami így a Single view Alpha értékével kerül megjelenítésre.

Touch handling

Az érintések kezelését két féle módszerrel tudjuk megoldani. Az első a controller osztályban előkészített eseménykezelő metódusok felülírása, a második egy kifejezetten erre a célra kialakított komponens használata ami a UIGestureRecognizer. A választás mindig attól függ, hogy milyen feladatot szeretnénk megoldani. A második megoldás konkrét esetekében lehet hasznos, amikor pontosan tudjuk, hol és milyen gesture-t szeretnénk figyelembe venni, míg az első megoldás bármire használható de jóval többet kell kódoni.

UIGestureRecognizer

A alaposztályból származik valamennyi gesture recognizer, melyek az egyes gesture-ök kezeléséhez használhatóak. Az alapelv minden esetben az, hogy felparaméterezzük az adott recognizer komponenst úgy, hogy fel tudja ismerni a számunkra kívánatos gesture-t. Ebben a példában a -t használjuk majd, mivel az érintést szeretnénk kezelni. Paraméterként az ujjak és az érintések számát kell megadni, amely alapján ha befut egy megfelelő érintés akkor a szintén paraméterként hozzárendelt esemény meg fog hívódni.

UIViewController

Egy korábbi cikkben már megismertük, hogy valamennyi view objektumhoz kapcsolódik egy kontroller objektum () amely kezeli a view-tól érkező valamennyi értesítést. Ez az objektum fel van készítve a különféle érintések kezelésére is. Az ehhez kapcsolódó metódusok a következők:

A következő eseménykezelő akkor hívódik meg amikor valamilyen érintés kiváltásra kerül azaz elkezdődik.

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

}

A következő eseménykezelő akkor hívódik meg amikor valamely már megkezdett érintésben elmozdítottuk valamelyik ujjat.

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

}

A következő eseménykezelő akkor hívódik meg amikor felemeljük valamelyik ujjunkat és az  érintés végetér.


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

}

Ez az eseménykezelő akkor hívódik meg ha az operációs rendszer valamilyen váratlan esemény miatt megszakít egy folyamatban lévő érintést.

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

}

POC megoldása

Az előzőekben leírtak alapján látható, hogy több lehetőség is kínálkozik a feladat megoldására. Bár elegendő lenne a jóval munkásabb, azaz a kontroller objektumban kezelt eseményekkel dolgozni, de itt rögtön az első POC-ban érdemes kipróbálni mindkét módszert, úgymond ötvözni őket a megoldás érdekében.

1 – Vegyünk fel két TapGestureRecognizert

Vegyünk fel két TapGestureRecognizert a MainView-ra és állítsuk be az egyiket egyujjas szimpla érintésre, a másikat kétujjas szimpla érintésre. Adjuk hozzájuk a következő eseménykezelőket:

- (IBAction)actionTap:(id)sender {
  if (touchInsideSimpleView && !switchRecognizeAny. on)
  {
    [self displaySingleView];
  }
}

A kontrollerben megírjuk az érintésre vonatkozó eseménykezelőket, ezek közül a touchBegan állítja be a touchInsideSimpleTap logikai adattagot. A displaySingleView a piros négyzetben aktualizálja a számlálót.

- (IBAction)actionTapByTwoFingers:(id)sender {
  if (touchInsideDoubleViewR && touchInsideDoubleViewL && !switchRecognizeAny. on)
  {
    [self displayDoubleView];
  }
}

A touchInsideDoubleViewR és L logikai adattagokat a touchBegan eseménykezelőben inicializáljuk, a displayDoubleView a két sárga négyzetben lévő számlálót aktualizálja.

2 – Írjuk meg az eseménykezelőket a kontrollerben

// -->
// ezt a metódust felülírva a view-n keletkező érintés eseményeket tudjuk kezelni
// bármilyen típusú érintés esetén meghívódik
// kétujjas érintés esetén az érintések halmazban mindkét érintés szerepel!!!
//
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  for (UITouch *touch in touches)
  {
    // -->>-- érintési pont a MainView dimenziójában
    CGPoint touchPointAtBase = [touch locationInView:self. view];
    // -->>-- ellenőrzés, hogy az érintési pont beleesik-e bármely subview-ba?
    if (CGRectContainsPoint([viewSimple frame], touchPointAtBase))
    {
      touchInsideSimpleView = TRUE;
    }
    if (CGRectContainsPoint([viewDoubleL frame], touchPointAtBase))
    {
      touchInsideDoubleViewL = TRUE;
    }
    if (CGRectContainsPoint([viewDoubleR frame], touchPointAtBase))
    {
      touchInsideDoubleViewR = TRUE;
    }
  }
}
// <--

Az érintés kezdetekor megvizsgáljuk, hogy az érintési pont a MainView-ra felrakott melyik sub-view-ban van benne és ezt a megfelelő logikai adattagokban elhelyezzük. A megkapott valamennyi érintési pontra el kell végezni ezt a vizsgálatot, és majd a recognizer-hez megírt eseménykezelőben ráérünk vizsgálódni, hiszen az úgyis csak akkor hívódik meg ha annyi ujjal és olyan érintés történt amit elvárunk.

// -->
// a metódus felülírásával az érintés befejezéséről kapunk értesítést
// Fontos!
// Ha gesture recognizer-t használunk XIB-ben vagy storyboar-on és szeretnénk
// használni ezt a metódust, úgy a recognizer komponensben állítsuk
// NEM kijelöltre a "Canceled in view" beállítást!
//
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  if (switchRecognizeAny. on)
  {
    if (touchInsideSimpleView)
    {
      [self displaySingleView];
    }
    if (touchInsideDoubleViewL && touchInsideDoubleViewR)
    {
      [self displayDoubleView];
    }
  }
  [self initTapFlags];
}
// <--

Az érintés végét csak azért kell implementálni, mert a külön kapcsoló bekapcsolt állapota mellett a specifikáció szerint magunknak kell eldönteni, hogy egy vagy két ujjas eseményként értékeljük az érintést (metszéspontok érintése másként értelmezett). Es gibt nichts gegen eine party einzuwenden, aber bitte nicht https://ghostwritinghilfe.com jedes we von freitag bis sonntag. Fontos továbbá, ahogy a fej kommentben olvasható, hogy ez az eseménykezelő csak akkor hívódik majd meg UIGestureRecognizer használata mellett, ha abban a “Cancel in view” opciót nem hagyjuk bekapcsolva.

.

Kategória: Általános | A közvetlen link.