I am making a custom iOS keyboard and have a UIControl subclass to represent my button. I am trying to get the same behaviour as the normal iOS keyboard:
User begins touch on one button
User drags over other buttons (need to detect this so they can highlight/dehighlight accordingly)
Register the actual keyboard "press" when the user lifts their finger; that is, the touch ends
I am testing using the touch tracking methods like this:
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super beginTrackingWithTouch:touch withEvent:event];
NSLog(#"Begin for %#", [self label]);
return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super continueTrackingWithTouch:touch withEvent:event];
NSLog(#"Continue for %#", [self label]);
return YES;
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super endTrackingWithTouch:touch withEvent:event];
NSLog(#"End for %#", [self label]);
}
These methods are all called, except they are only ever called on the UIControl where the touch began.
What is the best way to recognise touches coming and going across all my buttons? Do I have to do it all via the parent view?
I'll select a better answer if offered... but in case anybody finds this by search, I've managed to get what I need this way:
Set userInteractionEnabled to NO for the button class (UIControl subclass)
Don't override any touch methods in the button class
Implement touchesBegan:withEvent:, touchesMoved:withEvent: and touchesEnded:withEvent: in the view controller
On each event, extract the location from the UITouch object
Iterate over all of the button subviews and find the one containing the touch location:
- (MyButton *)buttonForTouch:(UITouch *)touch
{
CGPoint windowLocation = [touch locationInView:keyboardView];
for (MyButton *button in buttons) {
if (CGRectContainsPoint([button frame], windowLocation)) {
return button;
}
}
return nil;
}
Having determined which button the user is interacting with, make the view controller send messages to the relevant buttons to adjust their appearance
If appropriate, keep a reference to the UITouch instance in touchesBegan:withEvent: so you can be sure that you're tracking the same one in the other methods
I think that you should have a single big UIControl which has different subviews (like UIButton) but tracks touches by itself like you did already but finds out which subview to highlight depending on the touch position.
Related
I have a segmented control,with 5 segments..I want some event to be fired when a touch down happens on any of the segment and i mean tap and hold,without lifting the finger.
And when the user lifts his finger,it should reset
I have tried using touchesBegan and touchesEnded but i don't get the current selectedIndex in touchesBegan,here's my code
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
long oldValue = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (oldValue == self.selectedSegmentIndex )
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesEnded");
NSLog(#"selectedIndex%lu",self.selectedSegmentIndex);
[super touchesEnded:touches withEvent:event];
self.selectedSegmentIndex = -1;
}
Is there any way to achieve a touch down event effect for a segmentedControl,or any other alternative?
You can use touchDown: IBAction selector.
You can try to override:
- (void) setSelectedSegmentIndex:(NSInteger)toValue
Quite frankly, I do not think it is possible to do with UISegmentedControl. You do not get selected segment index until touchesEnded:withEvent: is called.
If you want to get in touchesBegan:withEvent:, my advise would be to write your own custom view that look like UISegmentedControl and gives you call back on touchesBegan:withEvent:.
As suggested by kientux above, adding transparent views or buttons on top of each segment in UISegmentedControl could be another potential solution to your problem but it would be very very tricky!
Good luck!
I tried to use custom UIControl in my view controller. My custom class which subclasses the UIControl and allocate the instance for my custom control and adding in to my view controller's view by following code
I have implemented following delegates which returns for YES to ensure the continuous touch.
- (BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super beginTrackingWithTouch:touch withEvent:event];
return YES;
}
- (BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super continueTrackingWithTouch:touch withEvent:event];
return YES;
}
- (void) endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super endTrackingWithTouch:touch withEvent:event];
}
- (void)cancelTrackingWithEvent:(UIEvent *)event
{
NSLog(#"Touch cancelled");
}
But - (void)cancelTrackingWithEvent:(UIEvent *)event get called when I am tracking. After that I should have to taken up my finger and drag again. then only I receive begin and continue tracking delegates
According to your question, While tracking,cancelTrackingWithEvent: get called. Right. Check your view or superview with Gesture call backs. If You've added pan Gesture, This type of problem will rise. That is your control touch will begin and get tracked upto this tracking change to panning.
To solve this issue, set tag to your view and cancel gesture call as below.
During your view creation
yourView.tag = CANCELVIEWTAG;
Cancel gesture if touch happen in your view.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view.tag == CANCELVIEWTAG) {
return NO;
}
return YES;
}
I have a problem as following, I've developed a ios app, to be concice , It has a UIViewController as parent, also it has a button , and the UIViewController popup a transparent UIView as a mask. When I click on the UIView(exactly within the underlying button boundary ) , the button could not receive any event(such as "touch up inside"), how could the button get the "touch up inside" event from transparent the UIView which is above the UIViewController?
This is not possible directly as the event triggered will not be for the button as button is not visible
(ie. another View is completly covering the button and is blocking the interaction with the user).
But i can give u a work around.
1.Declare a Bool Variable in your UIViewController
2.Implement the touches methods as shown below
- (void)touchesBegin:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
if(CGRectContainsPoint(button.frame, p) && !boolVariable) {
boolVariable = YES;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
// If the below condition is true then it mean there the user tapped on the same location as that of button..(touchesEnded and touchesCanceled was not called) so the event is just like touchUpInside
if(CGRectContainsPoint(button.frame, p) && boolVariable) {
boolVariable = NO;
[Here you can call the method which you wanted to call on touchUpInside of the button];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
- (void)touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
I was not able to test the able code on Xcode but i think it will work..
Note: The frame of the button should be with respect to the UIViewController.
Hope this helps u out :)
UIView instances, do not listen for touches. In order to get callbacks for events such as "touch up inside" are only sent to subclasses of UIControl
The most basic concrete subclass is UIButton.
Without code or more details about your app's setup, it's difficult to give better advice.
If a notification(i.e., a UIAlertView) appears while touching the screen (or home button is being pressed), ccTouchEnded will be called in game layer, but at the touch will already have ended.
How can I determine when the touch ends?
Check out the apple reference for UIResponder:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIResponder_Class/Reference/Reference.html
You probably want
touchesEnded:withEvent:
Hope this helps :)
You should implement ccTouchesCancelled. This will occur whenever a touch event is interrupted.
Just check how many touch objects you have.
if([touches count] == 0)
{
//NO TOUCHES
}
-Make your scene conforms to protocol CCTargetedTouchDelegate
-Add This line to init of your scene:
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:NO];
-Implement these functions:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
//here touch is ended
}
I have followed this great tutorial and I finally managed to implement a 3 independent rows scrollable interface.
I am left with a problem though, as the key of that tutorial is the use of method:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(#"in hitTest");
if ([self pointInside:point withEvent:event]) {
return _scrollView;
}
return nil;
}
in order to handle the scrolling even when outside the scrollview area.
In fact my rows are filled with UIButtons and their TouchUpInside events got mixed up with hit events. Is there a way to make this method recognize those events and reject them, letting them propagate to legitimate delegate?
You should probably implement the -hitTest:withEvent: method as follows:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *superView = [super hitTest:point withEvent:event];
if (superView == self)
return _scrollView;
return superView;
}
This will allow interaction within subviews of the UIScrollView.