In this tutorial we cover the following topics
- General information
- The family of touch notification methods
- Step 1: create a new project
- Step 2: set correct options in Attributes Inspector
- Step 3: add components
- Step 4: add a code
- Step 5: run the application
- Use
UIResponder
's subclasses to automatic gesture recognition- Step 1: add one more label
- Step 2: add a code, part one
- Step 3: add a code, part two
- Step 4: run the application
To be sure that we know what we are talking about, let's establish basic terminology.
- Touch. This term refers to any situation when finger is being placed on the screen, dragging across the screen, or being lifted from the screen. The number of touches involved in a gesture is equal to the number of fingers on the screen at the same time.
- Tap.A tap happens when we touch the screen with a finger (one or more) and then immediately lift it off the screen without moving it on the screen's surface.
- Gesture. A gesture is any sequence of events reported to the system from the time we touch the screen with one or more fingers until we lift all of our fingers off the screen. No matter how long it takes, as long as one or more fingers remain against the screen, we are still within a gesture (unless a system event, such as an incoming phone call, interrupts it). A gesture is ,,described'' by a system with a series of events. Events are generated when we interact with the device’s screen. They contain information about the touch or touches that occurred. To understand gesture concept it is worth to note that iOS and its librarys doesn’t expose any class or data structure that represents a gesture. In some sense, a gesture is a concept, idea. It is up to a running application to collect, examine the user input stream and make a decision if one is happening. Happily, there exists a class
UIGestureRecognizer
and its subclasses which can help us when we watch for common gestures. - Responder. Any class that has
UIResponder
as one of its superclasses is a responder. For example, all views and all controls are responders because bothUIView
andUIControl
is a subclass ofUIResponder
. If a responder handle a particular event, it will usually consume the event, which stops the event’s progression through the responder chain. If a responder doesn’t handle a particular event, it is usually supposed to passes that event up the responder chain. If a responder only partially handles an event, that responder will take an action and forward that event to the next interested responder in the chain. That is why, the following pattern is very common not only in Objective-C but generaly speaking in all event processing environments
1234567- (void)respondToEvent:(UIEvent *)event {if ([self isThisEventForMe:event]) {[self handleThisEvent:event];} else {[[self nextInterestedResponder] respondToEvent:event];}}
Whenever the user touches the screen for the first time, the system looks for a responder that has a method called
touchesBegan:withEvent:
. To catch this moment, the moment which may be a simple tap or begining of a complicated gesture, we have to implement this method in our view or your view controller. Both options are correct and it's up to us to make a correct decision based on our application's architecture. That method might look like
1 2 3 4 5 6 7 |
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // We don't care which finger touches the screen - we only want to get // the total number of taps. NSUInteger numTaps = [[touches anyObject] tapCount]; NSUInteger numTouches = [event.allTouches count]; ... } |
Apart this touchesBegan:withEvent:
method, there exists three more methods to detect other screen related events.
touchesEnded:withEvent:
This method is invoked when any of the user’s fingers is removed from the screen.touchesCancelled:withEvent:
This method is invoked when the user is in the middle of a gesture when something happens to interrupt it, like the phone ringing. This is where we can do any cleanup we might need so we can start fresh with a new gesture. When this method is called,touchesEnded:withEvent:
will not be called for the current gesture.touchesMoved:withEvent:
This method is invoked when the user is moving fingers across the screen. This method is called multiple times during a long drag, and each time it is called, we will get another set of touches and another event.
In all cases the NSSet
instance called touches
contains one UITouch
object for each finger that has just been added or removed from the screen or which has just moved or stopped moving. In other words, it tells us what changed between this call and the last time one of our touch notification methods was called.
The UIEvent
instance called event
has a property called allTouches
which is another set of touches.
This property contains one UITouch
object for each finger that is currently pressed against the screen, whether or not that finger is currently moving.
From given description we can infer that to track the activity of any given finger, we need to monitor the UITouch
object related with it. Each time a finger touches the screen for the first time, a new UITouch
object is allocated to represent that finger and added to the set that is passed in the allTouches
property of each UIEvent
. All future events that report activity for that same finger will contain the same UITouch
instance in both the allTouches
set and in the touches
argument, until that finger is removed from the screen. Have in mind that in the latter case, it will not be present if there is no activity to report for that finger.
- Step 1: create a new project
Create a new project- Select File | New | Project...
- From iOS tab select Single View Application on the template selection sheet.
- As the product name enter
iOS Touches Basic Application
- Set the Language to Objective-C, devices pop-up button to Universal and make sure the check box labeled Use Core Data is unchecked.
- Step 2: set correct options in Attributes Inspector
- Single-click either the background of the view we are working on or the View icon in the Document Outline, and then bring up the Attributes Inspector.
- On the Attributes Inspector, go to the View section and varify if both User Interaction Enabled and Multiple Touch are checked.
- Step 3: add components
- Place three labels on the screen: one for type of notification method, one for the number of taps and one for the number of touches.
- Add all necessary constraints.
- Double-click each label and for the first enter text Method, for the second Taps and for the third Touches.
- Single-click
ViewController.m
and add three outlets to the class extension at the top of the file as we did it many times before (use control drag from component (label) to the class source file). As a result we shoud get
12345678910111213141516171819202122#import "ViewController.h"@interface ViewController ()@property (weak, nonatomic) IBOutlet UILabel *labelMethod;@property (weak, nonatomic) IBOutlet UILabel *labelTaps;@property (weak, nonatomic) IBOutlet UILabel *labelTouches;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.}@end
- Step 4: add a code
- Single-click
ViewController.m
and add the following code to the class’s@implementation
section
1234567891011121314151617181920212223242526272829#pragma mark - helper methods- (void)updateLabelsFromEvent:(NSString*) eventName withTouches:(NSSet *)touches {self.labelMethod.text = eventName;NSLog(@"%@", eventName);NSUInteger tapsNumber = [[touches anyObject] tapCount];NSString *tapsMessage = [[NSString alloc]initWithFormat:@"%ld taps detected", (unsigned long)tapsNumber];self.labelTaps.text = tapsMessage;NSLog(@"%@", tapsMessage);NSUInteger touchesNumber = [touches count];NSString *touchesMessage = [[NSString alloc] initWithFormat:@"%ld touches detected", (unsigned long)touchesNumber];self.labelTouches.text = touchesMessage;NSLog(@"%@", touchesMessage);}#pragma mark - touch event methods- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {[self updateLabelsFromEvent: @"touchesBegan" withTouches:event.allTouches];}- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {[self updateLabelsFromEvent: @"touchesCancelled" withTouches:event.allTouches];}- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {[self updateLabelsFromEvent: @"touchesEnded" withTouches:event.allTouches];}- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {[self updateLabelsFromEvent: @"touchesMoved" withTouches:event.allTouches];}
- Single-click
- Step 5: run the application
In my case I got the following sequence of messages for a single tap and next for a touch and move
...single tap...
2017-05-01 23:30:36.348 iOS Touches Basic Application[1322:81996] touchesBegan
2017-05-01 23:30:36.350 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:36.351 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:36.355 iOS Touches Basic Application[1322:81996] touchesEnded
2017-05-01 23:30:36.355 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:36.355 iOS Touches Basic Application[1322:81996] 1 touches detected
..touch and move...
2017-05-01 23:30:41.581 iOS Touches Basic Application[1322:81996] touchesBegan
2017-05-01 23:30:41.582 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:41.582 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.825 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.826 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.826 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.842 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.843 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.843 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.865 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.866 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.866 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.881 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.882 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.882 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.897 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.898 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.898 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.913 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.914 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.914 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.937 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.938 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.938 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.953 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.954 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.954 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.969 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.970 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.970 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:42.994 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:42.994 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:42.994 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.010 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.010 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.010 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.034 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.034 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.034 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.050 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.050 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.050 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.076 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.076 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.076 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.098 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.098 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.098 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.114 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.114 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.114 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.130 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.130 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.131 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.146 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.146 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.147 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.162 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.162 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.163 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.194 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.194 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.194 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.226 iOS Touches Basic Application[1322:81996] touchesMoved
2017-05-01 23:30:43.226 iOS Touches Basic Application[1322:81996] 1 taps detected
2017-05-01 23:30:43.227 iOS Touches Basic Application[1322:81996] 1 touches detected
2017-05-01 23:30:43.651 iOS Touches Basic Application[1322:81996] touchesEnded
2017-05-01 23:30:43.651 iOS Touches Basic Application[1322:81996] 0 taps detected
2017-05-01 23:30:43.652 iOS Touches Basic Application[1322:81996] 1 touches detected
- Step 1: add one more label
- Place one more label on the screen below the previously added labels - we will use it to show swipe notifications.
- Add all necessary constraints.
- Double-click newly added label and enter text Swipe
- Single-click
ViewController.m
and add an outlet related to a new label as we did it before.
- Step 2: add a code, part one
- Single-click
ViewController.m
and add the following code at the end of the class’s@implementation
section
12345678910111213141516#pragma mark - automatic gesture recognition methods- (void)swipeVerticalDetected:(UIGestureRecognizer *)recognizer {[self updateSwipeLabel: @"Vertical swipe detected"];}- (void)swipeHorizontalDetected:(UIGestureRecognizer *)recognizer {[self updateSwipeLabel: @"Horizontal swipe detected"];}- (void)updateSwipeLabel:(NSString *) text{NSLog(@"%@", text);self.labelSwipe.text = text;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),dispatch_get_main_queue(),^{ self.labelSwipe.text = @""; });}
- Single-click
- Step 3: add a code, part two
- Single-click
ViewController.m
and update the code ofviewDidLoad
method
1234567891011121314151617- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.UISwipeGestureRecognizer *swipeV = [[UISwipeGestureRecognizer alloc]initWithTarget:selfaction:@selector(swipeVerticalDetected:)];swipeV.direction = UISwipeGestureRecognizerDirectionUp |UISwipeGestureRecognizerDirectionDown;[self.view addGestureRecognizer:swipeV];UISwipeGestureRecognizer *swipeH = [[UISwipeGestureRecognizer alloc]initWithTarget:selfaction:@selector(swipeHorizontalDetected:)];swipeH.direction = UISwipeGestureRecognizerDirectionLeft |UISwipeGestureRecognizerDirectionRight;[self.view addGestureRecognizer:swipeH];}
- Single-click
- Step 4: run the application
UIResponder
's subclasses to automatic gesture recognitionWe will continue to work with a previous project.