Pages

Friday, September 10, 2010

Detecting tap and double-tap with Gesture Recognizers

Now that I am finished with my book I am getting back to work on writing apps. In the original design for the current app that I am working on, I wanted a UIView that responded to taps and double taps. If you don’t already, know, as of iOS 3.2 there is a new way to handle user interaction events called gesture recognizers.

Gesture recognizers are basically exactly what they sound like; classes that you can use to recognize when a user has performed a gesture. Using gesture recognizers simplifies event handling because you do not have to write all of the code to track touches like you would have done in the past. You can simply create an instance of a gesture recognizer, configure it, and add it to a view. Then, when the gesture recognizer recognizes a gesture, it simply calls back a selector that you define.

UIGestureRecognizer is an abstract base class that you can use as a starting point to implement your own gesture recognizer. In addition to providing this base class, the iOS SDK provides several concrete UIGestureRecognizer subclass implementations that you can use to recognize the following gestures as documented in the iOS documentation:

Tapping (any number of taps)

UITapGestureRecognizer

Pinching in and out (for zooming a view)        

UIPinchGestureRecognizer

Panning or dragging

UIPanGestureRecognizer

Swiping (in any direction)        

UISwipeGestureRecognizer

Rotating (fingers moving in opposite directions)

UIRotationGestureRecognizer

Long press (also known as “touch and hold”)                

UILongPressGestureRecognizer

In my case, I wanted to create two different actions, one for when a user tapped a view and another for when the user double-tapped. This may seem straight forward as you can configure the UITapGestureRecognizer to call a selector when it detects a specific number of taps by setting the numberOfTapsRequired property. So, my idea was to use a UITapGestureRecognizer with numberOfTapsRequired set to 1 to call a selector called handleSingleTap and a second UITapGestureRecognizer with numberOfTapsRequired set to 2 to call a selector called handleDoubleTap.

This was all well and good. When I built and ran my application, single tap event handling worked like a charm. Every time I tapped on the view, I saw the behavior that I expected. The problem occurred when I tried to double tap. The double tap selector was called as expected, however, the single tap selector was always called first. I guess that, in hindsight, I should have expected this as it is impossible to tap twice without tapping once. One particularly important thing that you should keep in mind when using gesture recognizers is that they are not a part of the responder chain. If you are relying on handling events using the responder chain and you introduce gesture recognizers, events that are recognized by the gesture recognizer will not be sent through the responder chain.

What I needed was a way to set up a relationship between the two gesture recognizers. I needed to be able to detect if a double tap was happening and if it was, to ignore the single tap gesture. Fortunately, Apple thought ahead and designed a way to do this into the architecture. This is a case where you only want the single tap selector called if the double tap gesture recognizer fails. To implement this, you send the message requireGestureRecognizerToFail: to the single tap gesture recognizer with the double tap recognizer as its argument. This tells the single tap gesture recognizer to wait to fire until the double tap gesture recognizer transitions to the UIGestureRecognizerStateFailed state. Doing this solved the problem. At this point, the correct selector was called when a double tap was recognized and the single tap selector was not called.

However, this caused another issue. The issue was that when a user would single tap, the action that would result from a single tap was delayed as the single tap gesture recognizer was forced to wait to ensure that the double tap gesture recognizer failed before calling the single tap selector. This behavior is documented in the Event Handling Guide for iPhone OS. For many applications, this may be acceptable, however the lag between a single tap and the gesture recognizer calling its selector was too long for my application. I searched for a way to adjust the timeout for a gesture recognizer to transition to UIGestureRecognizerStateFailed to no avail. It seemed that the only way to get the behavior that I wanted was to write some custom gesture recognizers.

In the end, I decided to not use the double click gesture recognizer and to handle my user interactions differently by not requiring a double tap at all. So, keep in mind that it is possible to set up dependencies between gesture recognizers however when implementing discrete behaviors for single and double tap, you may want to go a different route. Gesture recognizers are a convenient way to react to user input and can be configured in a variety of way depending on the needs of your application. If worst comes to worst, you can always write your own concrete subclass of UIGestureRecognizer.

15 comments:

  1. Hello, I'm looking forward to your book, Professional iphone and ipad database programming. Could you go further into detail about the book, what subjects it will cover; for example SQLite?

    I am also interested in knowing whether your book talks about how to handle large databases and the correct way to load 5,000 records into a tableview without loading it all into objects or NSMutableArray's.

    In addition, would your book talk about how to properly scale iphone/sqlite database projects.

    I would also be interested in knowing if your book covers things such as using in-app purchases to download database updates -- ie: A football app which downloads the latest team roster for this year, etc.

    Any help or notes on this would be great!

    Thanks for your help

    ReplyDelete
  2. Thanks for the comment Jack. The book covers SQLite, using/customizing/performance of UITableView, Core Data, XML Web services along with some other ancillary topics like the new UI widgets for the iPad, Core Location, and MapKit.

    The book doesn't directly address large databases, but it does cover both of the major data access strategies (SQLite and Core Data) extensively.

    With regard to scaling iPhone database projects, I'm not sure what you are asking. In general, designing a scalable system involves building the software in such a way that you can add extra hardware and gain a proportional processing advantage. This is not really applicable when you are designing software to run on a client device like the iPhone or iPad.

    The book doesn't cover in-app purchase, but that may make a good topic for a second edition, or at least a blog post.

    ReplyDelete
  3. Thanks Patrick, I will be buying the book. When I mean scaling iphone database projects, what I mean is handling a database table with thousands of records. Obviously loading a table into memory is silly.

    I've been reading about Core Data and NSFetchedResultsController as one way to help manage/display a tableView without causing too many problems on memory management or slowing down scrolling.

    The in-app purchase thing isn't a major issue, but its a nice to have -- for example my app has "scenarios" which are just sqlite databases which you can buy and download... but this isn't fixed yet.

    I'm looking forward to your book!

    ReplyDelete
  4. Will you be discussing how to use OFFSET/LIMIT vs Core Data in terms of SQLite.

    Also, will you be talking about migrations, how to do parent-child relationships with the same entity (ie: Articles can have parent Articles).

    Also, when is the book being released? Will you be doing an Amazon videocast for it?

    ReplyDelete
  5. I quickly skimmed your book, but it does not cover using offset/limit handling for large databases and table views. Although I am still working through your book (which is great because it concentrates on real world issues).

    ReplyDelete
  6. Sorry about that. Maybe I can cover using OFFSET/LIMIT with SQLite in a blog post. My fear is that not many people will use SQLite unless they have very stringent performance requirements.

    ReplyDelete
  7. Thanks Patrick. I look forward to it. My main reasoning for learning about OFFSET/LIMIT handling batch displays for SQLite is because I'm wanting to build a sports simulation game which requires a large data set -- whilst I could do this in Core Data, the problem I'm having is migration. Ideally, I want to use Django or RoR to do the mass input of data -- and then use SQLite in the iphone to handle the rest of the stuff.

    I think I may just stick to Core Data for the moment, but your book is very interested and easy to read thus far.

    ReplyDelete
  8. When will you do the blog post on using offset/limit for TableView::cellForRowAtIndexPath as well as using it in a general context?

    Thank

    ReplyDelete
  9. Patrick, I'm enjoying your book on iPad/iPhone development. A steep learning curve for me: I'm a .NET developer, but my client needs a small iPad app. Where I'm confused is this: you say in preface that part 1 covers how to connect to large databases: Oracle, SQL Server, mySQL. I need to connect to SQL Server. But reading forward, I'm not actually finding any references to MS SQL Server in the actual text. Am I missing it? It's not in the index at the end of the book either. Would appreciate it very much if you provided some advice - this was the very reason I bought the book!

    Thanks so much,

    Anatoly Molotkov, MCPD

    ReplyDelete
  10. Anatoly,

    I don't cover connecting directly to a SQL server database in the book. What I did cover is getting the data out of a database and getting it into on iOS device using SQLite. If you can dump data to a text file, you can then read it into a SQLite database using the command line tools provided.

    As far as connecting to SQL server, your best bet would be to expose whatever data/services that you need from the database via .Net web services. Then, build your iOS app to work with those web services.

    ReplyDelete
  11. Patrick, thanks for the clarification. I feel the way the books is presented at Amazon is misleading in that way - I bought it for this reason specifically. Are you saying that there is no way to connect to SQL Server from an iPad application directly?

    thanks so much,

    Anatoly

    ReplyDelete
  12. According to Microsoft's web site, you can enable Native XML Web Services in SQL server. They say, "Microsoft SQL Server 2005 provides a standard mechanism for accessing the database engine using SOAP via HTTP. Using this mechanism, you can send SOAP/HTTP requests to SQL Server to execute:

    Transact-SQL batch statements, with or without parameters.
    Stored procedures, extended stored procedures, and scalar-valued user-defined functions."

    Check out their web site at http://msdn.microsoft.com/en-us/library/ms345123(v=sql.90).aspx

    I'm not a SQL Server expert, but I would think that this is probably your best bet.

    ReplyDelete
  13. Patrick, thanks again! We are worried about the ramifications of configuring HTTP services and having to distribute this new configuration to all existing users. Also, that URL says "SQL Server 2005-native Web services require Microsoft Windows Server 2003 as the operating system" - whereas some of our users are on Windows XP, Vista, or 7. In SQL Server 2008, the feature is being deprecated. Is there no way to access SQL Server via ODBC or the like?

    Thanks,

    Anatoly

    ReplyDelete
  14. There is an open source ODBC wrapper library for iOS that you can find here http://odbcrouter.com/iosproxy.

    As I said, I'm not a SQL Server expert so I don't know what you have to configure to support ODBC connections. Also, since Objective-C is a superset of C, you can use any C libraries in building iOS apps. If the library that I've mentioned doesn't work, I'm sure that you could use any number of other C ODBC libraries.

    ReplyDelete
  15. Patrick thank you so much for this elaborate post! It really saved me a lot of time. Thanks. Nur

    ReplyDelete