I'm pretty new to iPhone programming, so I'm looking for any pointers (no pun intended), links, search terms, etc. on how to make a very simple app that counts how many times one finger touches the screen and how long between each tap.
Thanks for any help, I know this resembles a lame question seeing as how I have really accomplished anything yet.
rd42
In touchesBegan, use:
NSSet *allTouches = [event allTouches];
int tapCount = [allTouches count];
to retrieve the tap count.
You would then need to save the timestamp of the first tap, and find the difference between the next tap in order to get the time elapsed between them.
You'll want to use touchesBegan: and touchesEnded: methods of UIResponder.
All UIViews are UIResponders. So just override those methods on whatever UIView you want the users to touch. Note that there are four touches methods:
touchesBegan:
touchesMoved:
touchesEnded:
touchesCancelled:
If you override any of them, you are required to override all of them. So for the ones you don't care about, just have practically-empty implementations that just call [super touchesMoved: arg1 withEvent: arg2], etc.
Related
How do I get the co-ordinates of 2 touches on an iPhone? (both co-odiantes)??? This is killing me... any sample code would be great. Thanks.
If you are using touchesBegan:withEvent: and its siblings, you will be passed an NSSet object containing all the touches. You can get an NSArray using allObjects method on the set. You can retrieve individual UITouch objects using objectAtIndex: method. The UITouch object can give you coordinates based on any view's frame through the method locationInView:. The call will be on the lines of CGPoint point = [touch locationInView:self.view];. Do this for all the touches in the array.
If you are using gesture recognizers, the gesture recognizer object has a method numberOfTouches that gives you the number of touches and you can retrieve the location of each touch using locationOfTouch:inView:.
check touches began, touches moved, touches ended, and touches cancelled. here is the link for this UIResponder class reference
i want to make something like this
http://www.htmlcodetutorial.com/images/images_famsupp_220.html
and i want to know if there is class to do it in objective c?
thx
if i understood you correctly, you want to have several sensitive points on an image view?
try adding
-(void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
NSLog(#"Touches: %#", touches); //examine this!
}
to your code.
it provides UITouch instances in the touches-set, each having an location point for your views.
checkout
UITouch locationInView:;
regs.
UIWebView supports image maps. If you want a pure Objective-C solution I would recommend using an UIImageView and capture touches where you would like to map the image. Once some one touches the view you can then call the appropriate action.
I have a UIWindow with windowLevel set to UIWindowLevelStatusBar+1. This window has a single semi-transparent view that blocks the status bar. I need to sometimes pass touch events from the view on to the status bar. Any ideas?
So, it seems to be doable with a custom subclass of UIWindow overriding hitTest:withEvent: that manually detects a touch in the subview, and always returns nil.
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([event type]==UIEventTypeTouches) {
UIView *v=[super hitTest:point withEvent:event];
if (customSubViewthatCoversStatusBarOnly==v)
//doLotsOfCoolStuff
}
return nil;
}
Status bar recognizes all touches, so there is no breakage with scroll-to-top, return to call, VoiceOver, etc.. And I still get to intercept taps on statusbar.
I hacked this up just now. I will probably upload an update to App Store later this week with a more mature version of this, will see how much complaining Apple will do.
EDIT - 7th April:
Was approved by Apple. Works flawlessly.
You might find this component over on github helpful.
Otherwise, Cocoa with Love blog post is really useful to read perhaps.
As far as I understand this, you should use - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event to implement that behavior. Basically, you either return self if you want to handle the touch event or [super hitTest:point withEvent:event] to let the status bar handle the touch event.
Check out the UIView Class Reference for more.
EDIT: As Jonathan mentioned, Apple might not approve this.
Is there a simple way to detect if someone touched a UIScrollView without having to disable user interactions?.
I know this has been answered a few times before, but every answer I find is somebody wanting to detect hits in an image. I don't have an image. What I do have is a scroll view with a number of text fields embedded in it. They used to be inside a UIControl, from which I could detect a touchDown and call resignFirstResponder on all my textfields (for when the keyboard is up). But when they are in a UIScrollView, I can't seem to find a simple way to do this.
I don't really want to have to write code to do the scrolling myself, which is what I assume I have to do if I disable user interactions and grab the touchesBegan message. But I may be wrong. I'm still a little new at this, but this is the first time I haven't been able to figure out the answer by reading the code doc and googling ...
Thanks for any help,
J
Override touchesBegan, do your processing, and call the super implementation so that the scroll view still gets the message.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//call resignFirstResponder
[super touchesBegan:touches withEvent:event];
}
Ok, now I feel a little silly answering this myself. I'm sure the other suggested answer would have worked also, but I found something else while browsing in other topics which totally fixed this for me.
I added this to my viewDidLoad:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(backgroundTap:)];
[scrollView addGestureRecognizer: singleTap];
Where backgroundTap is
- (IBAction)backgroundTap:(id)sender
is the function I was originally calling from touchDown in UIControl to resign all keyboard firstResponders.
This works perfectly and adds very little code. And I DON'T have to disable user interactions! Yay!
Thanks all for your comments and help.
J
I fiddled with this for a while, and found that simple touchesBegan and touchesEnd type stuff didn't really work.
They don't work because because of the gestures. For example touchesEneded never fires when you swipe to scroll (at least not in the UIScrollView). So while you can detect when a touch event starts, you'll never know when it ends.
I wasn't real keen on including a gesture recognizer for simple stuff and discovered that you can find out about the state of the scrolling better from interacting with the UIScrollView directly.
And it's not that painful. Just add the protocol, then add the functions you need for detection.
In the view (class) that contains the UIScrolView, add the protocol, then added any the functions from here to your view (class).
Example:
// --------------------------------
// In the "h" file:
// --------------------------------
#interface myViewClass : UIViewController <UIScrollViewDelegate> // <-- Adding the protocol here
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView;
// --------------------------------
// In the "m" file:
// --------------------------------
#implementation BlockerViewController
UIScrollView *scrollView;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSLog(#"end decel");
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
NSLog(#"end dragging");
}
// All of the available functions are here:
// https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html
Can we call different actions / delegates in response to the two different events of
A tap on a UIButton
A tap-and-hold on a UIButton
?
Yes, it's reasonably easy to implement this using a UILongPressGestureRecognizer (on iPhone OS 3.2+). A long press will be handled by the gesture recognizer, and a short tap will pass through to the button's normal action.
For example, I subclassed UIButton and added the following method for specifying a long touch action to go along with a tap (longPressGestureRecognizer is an instance variable):
- (void)setLongTouchAction:(SEL)newValue
{
if (newValue == NULL)
{
[self removeGestureRecognizer:longPressGestureRecognizer];
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
}
else
{
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:[[self allTargets] anyObject] action:newValue];
[self addGestureRecognizer:longPressGestureRecognizer];
}
}
I then could do the following to set both short tap and long press actions that will be handled by the same target:
[undoButton addTarget:self action:#selector(performUndo:) forControlEvents:UIControlEventTouchUpInside];
[undoButton setLongTouchAction:#selector(showUndoOptions:)];
As you can see, this is useful for the undo buttons you see in title bars of many iPad applications.
Brad Larson's answer looks pretty good but here's another one that might give you a bit more flexibility/control of what you want or might want to do.
You subclass UIButton, you override the touchesBegan and touchesEnded methods so that when the user starts a touch you call
[self performSelector:#selector(detecetedLongTap) withObject:nil afterDelay:1.0];
and in the touchesEnded you call:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(detecetedLongTap) object:nil];
to cancel the event if the finger was lifted too soon.
You can get full code for this in this blog post:
http://www.isignmeout.com/adding-long-tap-functionality-uibutton/
The best solution I can think of, is to create another class, and subclass UIButton. Then on Interface Builder (if that's what you're using), you can set the button's class to the custom class you just created.
So in this new class, you have to override a method called
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
This is basically telling you that someone pressed down on your button. The touches is an NSSet and it holds all the information for all the fingers that are pressing down on the screen. If you only are interested in the one that's pressing on the button itself, you'll probably have something like:
NSSet *myTouches = [event touchesForView:self.view];
So now that you have the touches that correspond to your button, you have to find out how many times the user tapped on that button. You do that with something like:
NSUInteger numTaps = [[myTouches anyObject] tapCount];
If this number is 2, that means the user just double tapped your button. Now comes the next part. How do you know if the user is holding that button? Well when the user lets go of the screen, another method gets called. You'll need to override that one as well:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
This is where you know if the person has stopped touching the screen or if his finger is still on it. If his finger is still on it, then this event hasn't been called yet.
Now enough with the background
Here's my suggestion to you. I suggest you override the touchesBegan: method and check if the number of taps in the button is 2. If so, then start a timer that does what you need it to do, for as long as you need it to be done, and then on the touchesEnded: method, you'll go ahead and stop that timer, and this way you will have done whatever it is that you needed to do, for as long as you needed to do it OR as long as the user has held on to the button.
I hope this helps, obviously I didn't write the whole code for you, you'll have to experiment and research that stuff, but if you have any questions, I'll be happy to lend a helping hand :)