Touches

April 26th, 2009 by Chris Leave a reply »

Touches are handled by three methods:

  • touchesBegan:
  • touchesMoved:
  • touchesEnded:

To have access to touches you need to code these 3 methods (or one/two of them).

The code is very simple:

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

touchesBegan: will be called whenever user touch the screen with one finger, and each time new touch is declared for example with the second finger, nose ear, battery, foot…

touchesEnded: will be called whenever any single touch is ended

touchesMoved: hmmm it’s not so clear as you might think. Yes, this method will be called if any finger moves while touching the screen, but on real device this method is called almost immidietely after touchesBegan. Why is that? On iPhone simulator mouse plays role of the finger, so you can press and release the mouse/touch-pad button and both touchesBegan and touchesEnded will be called, but not touchesMoved provided that you won’t move a pointer. If you touch an iPhone / iPod screen in 99% the coordinates of where the touch started and ended will be different, so meanwhile touchesMoved will be called.

You probably woud like to know what are the touch coordinates, it’s very easy to get them. Extend any of above three methods with this code:

	UITouch *touch = [touches anyObject];
	CGPoint touchCoordinates = [touch locationInView:self.view];

touchCoordinates.x and touchCoordinates.y give you information you need.

If you would like to have details of more than single touch (you can touch the screen with as many fingers you want), this might be helpful:

	NSSet *allTouches = [event allTouches];
	UITouch *singleTouch;
	for (int i = 0; i < [allTouches count]; i++) {
		singleTouch = [[allTouches allObjects] objectAtIndex:i];
		// whatever you need goes here
	}

If you would like to simply know how many fingers are touching the screen use [allTouches count]; with first line of above example.

If you want to do something when users double tap the screen for example try:

	if ([touch tapCount] == 2) {
		// whatever you need goes here
	}

You can use only tapCount for the single touch. Taps aren’t counted for multiple touches.

Now something more interesting. How to check if user touched an object like image, label…? Before you enter any code in the attributes in Interface Builder of your item enable option: User Interaction Enabled and Multiple Touches if needed. views have User Iteraction enabled by default. You can also do it in XCode:

	myObject.userInteractionEnabled = YES;
	myObject.multipleTouchEnabled = YES;

Once User Interaction is enabled you can use if statement to perform any task:

	if ([touch view] == myObject) {
		// whatever you need goes here
	}

touchessimulator I guess it’s all you need to know about touches. I prepared for you a project you can download. It gives you all information:

  • where the touch began (x,y)
  • where the touch moved (x,y)
  • where the touch ended (x,y)
  • how many fingers touched the screen (in touch began)
  • the tap count
  • and a Move Me square you moves by moving your finger

xcodeproj

Download the project

Advertisement

17 comments

  1. codefire says:

    thankyou very much chris,,,,,,,,,,
    very efficeint manner to understanding the touch button..
    relly i like it..

  2. ujku malit says:

    hi
    i want to play sound on – (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    pliss help me

    M
    #import “mainview.h”

    @implementation mainview

    - (IBAction)A1 {
    SystemSound = [[sounds alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”A1″ ofType:@”mp3″]];
    [SystemSound play];

    }

    - (IBAction)B2 {
    SystemSound = [[sounds alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”B2″ ofType:@”wav”]];
    [SystemSound play];

    }

    - (IBAction)C3 {
    SystemSound = [[sounds alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”C3″ ofType:@”wav”]];
    [SystemSound play];

    }

    - (IBAction)D4 {
    SystemSound = [[sounds alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”D4″ ofType:@”mp3″]];
    [SystemSound play];

    }
    - (IBAction)GR1 {
    SystemSound = [[sounds alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”GR1″ ofType:@”mp3″]];
    [SystemSound play];

    }
    - (IBAction)AL1 {
    SystemSound = [[sounds alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”AL1″ ofType:@”mp3″]];
    [SystemSound play];

    }
    @end

    H

    #import
    #import
    #import “sounds.h”

    @interface mainview : UIView {
    sounds *SystemSound;

    }
    - (IBAction)A1;
    - (IBAction)B2;
    - (IBAction)C3;
    - (IBAction)GR1;
    - (IBAction)AL1;
    - (IBAction)D4;
    @end

    • Chris says:

      I don’t think it’s good to keep allocating sounds again and again.

      You should alloc them in viewDidLoad, or loadView, or any other method you will call only once, and layer only use:

      [sound1 play];
      [sound2 play];

      much better for memory.

      What do you want to achieve? I guess you should read articles about playing sounds with systemSound, and play sound with repeat that means, touchBegan starts playing touchEnded stops playing.

      I’ve given you a tip, write your own solution.

      • blacksheep says:

        hi, i wrote an app in the same complicated way.
        could you please give an example how to alloc several sounds once?

        bell_left_Sound = [[SoundEffect alloc] initWithContentsOfFile:[mainBundle pathForResource:@"bell_left" ofType:@"caf"]];
        bell_right_Sound = [[SoundEffect alloc] initWithContentsOfFile:[mainBundle pathForResource:@"bell_right" ofType:@"caf"]];

        - (IBAction)bell_left: (id)sender {
        SoundEffect *currentSoundEffect= bell_left_Sound;
        [currentSoundEffect play];
        }
        - (IBAction)bell_right: (id)sender {
        SoundEffect *currentSoundEffect= bell_right_Sound;
        [currentSoundEffect play];
        }

        thanx 4 help

  3. Al says:

    Hi Chris,
    Firstly let me thank you for your great effort helping us non experts.
    I’m after finding your site while looking for a way to have checkboxes in rows. These checkboxes are images and are on the left of the rows (the right side has the reordering accessory). The checkbox images are to change when tapped, and also tapping the text in the center of the row is to bring the user to a new screen. So 3 events on each row, 1. tapping checkbox image on the left, 2. tapping the text in the center of the row, 3. the reorder accessory.
    Using your code above should I easily be able to capture tapping on the checkbox image?
    Thanks for any help!
    Tomorrow I’ll be sending on your site to a lot of colleagues…I can’t right now, typing all this on an ipod touch :-)
    Great site,thanks again!

    • Al says:

      P.s. I’m hoping I can take the checkbox image as an object and just capture touches on that, as you’ve described. Therefore I’m hoping I won’t have to deal wit coordinates. Is this possible?
      Thanks!

      • Al says:

        P.p.s sorry, but when I say rows I’m reffering to cells (UITableViewCell).
        Thanks!

        • Chris says:

          Don’t play with coordinates, just [touch view].
          Make sure userIteractionEnabled = YES.

          Regards
          Chris

          • Al says:

            Hi Chris,
            Thanks for the reply.
            But userInteractionEnabled won’t apply to an imageview:
            cell.imageView.userInteractionEnabled = YES;
            Any ideas?
            Thanks!
            Al

          • Al says:

            I’m getting:
            error: request for member ‘imageView’ in something not a structure or union.

  4. Yusuf says:

    Hi, firstly, what a great site, thanks for building it! Secondly, I would like to know how to handle moving two objects at once – like in a two player game, where the screen is effectively “split in half”, so when detecting a touch from one side of the screen it moves the object on that side of the screen in the x direction, and if a touch is detected on the other side of the screen, it moves another object in the x direction instead. Both to be able to move at the same time as each other – like a two player game.

    Thanks, keep up the amazing work!

    • Chris says:

      I guess you could treat both touches like different objects and remember them but in that case it’s quite simple.

      NSSet *allTouches = [event allTouches];
      UITouch *touch;
      CGPoint touchPos;
      for (int i = 0; i < [allTouches count]; i++) {

      if ([allTouches count] > 2) break; // cheaters

      touch = [[allTouches allObjects] objectAtIndex:i];
      touchPos = [touch locationInView:self.view];

      if (touchPos.y < 240) {
      // move the object from the upper side of iP’s screen e.g.:
      pingping1.center = touchPos;
      }
      else {
      // move the object from the lower side…
      pingpong2.center = touchPos;
      }

      For sure you need to add something like above to both touchBegan and touchMoved

      regards, Chris

  5. Jamie C says:

    Hello,

    Your site is fantastic, I have recommended it to many people starting with xcode.

    Now for my question, I would like to know how you would set up a CGRect with an image such as a Television, so when you touch it anywhere (on the iPhone) it loads the first image, of say the glass cracking, and then if you touch it again it breaks more, and then if you touch it again it blows up.

    How can you make it register these multiple touches? And cause them to have multiple effects? And can you repeat this many times on the same picture?

    Thank you in advance,

    Jamie C

    • Chris says:

      Everything is explained in the tutorial above, but if you really can’t work it by yourself:

      It was hard to understand you.
      Do you mean, the TV blows up when you are touching it with three fingers (A) or you’ve touched it for the 3rd time (B)?

      (A):
      in touch began method:

      NSSet *allTouches = [event allTouches];
      UITouch *singleTouch;
      int fingersTV = 0;
      for (int i = 0; i < [allTouches count]; i++) {
      singleTouch = [[allTouches allObjects] objectAtIndex:i];

      if ([touch view] == myObject) {
      fingersTV++;
      }

      }
      switch (fingersTV) {
      case 0:
      break;
      case 1:
      tv.image = [UIImage imageNamed:@"tvStart.png"];
      break;
      case 2:
      tv.image = [UIImage imageNamed:@"tvGlassBreak.png"];
      break;
      default: // 3 or more
      tv.image = [UIImage mageNamed:@"tvDead.png"];
      }

      (B)
      in header add
      int tvTapCount;

      and in touchBegan:

      if ([touch view] == myObject) {
      tvTapCount++;
      }
      switch (tvTapCount) {

      }

      if you are talking about 3 fast taps, think about [touch tapCount]

  6. pierre says:

    thanks for your website. I’m new in the iPhone app development. how do you disable a UIButton created in IB and then how to enable it later again. The other question how do you make sure that when you use the numbers keyboard that only numbers can be used for your UITextFiled created in IB, thanks let me know

    • Chris says:

      Hi Pierre!
      Right now I have my computer in repair, so I can debug nothing.
      But when you want to enable/disable something created in IB, you need to create an IB outlets in .h file:

      IBOutlet UIButton *myButton; and you have to connect myButton with UIButton in IB. Than somewhere in code:

      myButton.enabled = YES;
      myButton.enabled = NO; // disabled

      or:
      [myButton setEnabled:YES]; // or no

      hmm numbers only in UITextFiield. I’ve never had any need to parse data. You can set the keyboard type to numeric, but as long as I remember there is no decimal point and no retrunButton, but using a normal keyboard I guess you could do something like this:
      (I assume your UITextField is called myTextField)

      -(IBAction)parseTextField {
      myTextField.text = [NSString stringWithFormat:@"%f",[myTextField.text floatValue]];
      }
      and above method is called whenever text was changed – it can be done in interface builder

      Above will return float, to get an integer instead use
      @”%i” and [myTextField.text intValue];

      regards
      Chris

Leave a Reply