How do i pass an event from a subview to trigger UIControlEventTouchUpInside of its superview(a subclass of UIControl)? The subview has a triangular area of a certain size. If I remove my finger from within the triangle I want the UIControl superview to trigger its action. I tried to forward touchesEnd from the subview to the superview with nextResponder but it didn't work.
To programmatically fire an event on a UIControl, use this method:
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents
for example:
- (void)touchUpInside {
// will need to cast superview to UIControl
[[self superview] sendActionsForControlEvents:UIControlEventTouchUpInside];
}
more info on this method in the docs.
Related
I have a UIView parentView which implements a UITapGuestureRecognizer and does something when tapped. parentView has a sub view called childView which also implements a UITapGuestureRecognizer and does something when tapped.
There is an instance when I have to turn off the childViews UITapGestureRecognizer during an animation for a slight moment, and I noticed when it is turned off and I tap childView, the tap gets intercepted by parentView. Also, I have a toolbar attached to the top of this view that doesn't have any gesture recognizer attached to it, and it's touches get passed parentView (the buttons will barely work). I'm wondering is it possible to disable this without having a reference to the parents UITapGestureRecognizer?
I've tried using the exclusiveTouches property of UIView set to yes and it doesn't work. Any suggestions would be appreciated.
In the parent gesture recognizers, implement the UIGestureRecognizerDelegate, and implement the following method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isKindOfClass:[ClassThatYouWantTouchesBlocked class]])
{
return NO;
}
else
{
return YES;
}
}
Replace ClassThatYouWantTouchesBlocked with the class that you want its touches to be ignored.
What if never turn off the ChildView's tap gesture recognizer? if it is animating return from the child's tap gesture method without doing anything.
If it is an imageView than isAnimating property might come in use.
Add gesture recognizer to blocker child.
self.blockerView.addGestureRecognizer(UITapGestureRecognizer())
I have a scrollview in my Main view and I have three subviews on my scrollview. And I have UIButtons in all my subviews.
Now, I want to drag those buttons from one subview to another subview (while dragging the buttons, the scrollview should not scroll).
How do I do this?
I'm not completely sure if this snippet works for this particular case (an UIControl inside a UIScrollView), but my understanding of UIResponder chain suggests me that it should :)
- (void)viewDidLoad { // or wherever you initialize your things
...
// Add swipe event to UIButton so it will capture swipe intents
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] init];
[panGR addTarget:self action:#selector(panEvent:)];
[button addGestureRecognizer:panGR];
[panGR release];
}
- (void)panEvent:(id)sender {
button.center = [sender locationInView:self.view];
}
If this works (can't test it right now, but it did work for me in a similar situation), then you should add more code to handle the drag & drop related events (maybe disable Clip Subviews option in the UIScrollView, add the button to the new superview if the location intersects with the CGRect of the destination, return the button to the original location if it doesn't, etc).
So, what's happening in those lines? When you begin touching the UIButton, the order doesn't get to the UIScrollView because the event could follow as a touch event (handled by the UIButton), or as a pan event (handled by the UIScrollView). When you move your finger, the event is dismissed by the UIButton's responder because there's no Gesture Recognizer that knows how to proceed if the finger is moved.
But when you add a Gesture Recognizer to the UIButton who actually knows what to do when the finger is moved, everything is different: the UIButton will not dismiss the event, and the UIScrollView will never realize that there was a touch moving over it.
I hope my explanation is accurate and comprensible enough. Let me know if a) it doesn't work or b) there's something unclear.
Good luck :)
Try
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if (allowAppDrag && [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
return NO;
}
return YES;
}
I have a view and I want to enable a user to draw on it after tab a UIButton, I have do this usingUIPanGestureRecognizer , add a UIPanGestureRecognizer this view after UIButton touch but the issue is how I can remove this UIPanGestureRecognizer after I done my drawing and re-touch the UIButton ??
UIView has a method called
- (void)removeGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
alternatively, you can disable the UIGestureRecognizer temporarily by removing its callback using the method
- (void)removeTarget:(id)target action:(SEL)action
UIPanGestureRecognizer gestureRecognizer.cancelsTouchesInView = NO;
If you have multiple pan gesture recognizers for a view, you can tag a specific one with an associated object.
What is objc_setAssociatedObject() and in what cases should it be used?
So at the top of your .m file, you'd put
static char overviewKey;
then right before you add your UIPanGestureRecognizer to the view, you'd tag it with a string.
objc_setAssociatedObject(panGesture, &overviewKey, #"pan gesture for drawing", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[someView addGestureRecognizer:panGesture];
When you want to delete the UIPanGestureRecognizer, you'd go through all the gesture recognizers in that view, find the one with the string, and delete it.
for (UIGestureRecognizer *gesture in someView) {
NSString *gestureTag= objc_getAssociatedObject(gesture, &overviewKey);
if (gestureTag==nil) {
continue;
}
if ([gestureTag isEqual:#"pan gesture for drawing"]) {
[ someView removeGestureRecognizer:gesture ];
}
}
My app has a custom UITableView. In the cellForRowAtIndexPath delegate method of its UIViewController I am instantiating custom UITableViewCell objects that contain multiple custom UILabels (actually a sub-class of OHAttributedLabel) as subviews of the content view.
I have tried setting userInteractionEnabled = YES on the label, then adding touch events in the view controller, but that isn't working.
Ideas?
Thanks
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if (CGRectContainsPoint([self.site frame], [touch locationInView:self.view])){
//do whatever you want
}
}
Or
UILabel *label = =[UILabel alloc]init];
label.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture =
[[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelTap)] autorelease];
[label addGestureRecognizer:tapGesture];
A UILabel isn't a UIControl so you won't get events on UIControlEventTouchUpInside or similar. Why not use a button instead? You can make it look exactly like a label.
Regardless you will probably need to set addTarget:action:forControlEvents: and tag on the UIButton in your cellForRowAtIndexPath: method. In that method, detect which cell's button was tapped by examining the tag value.
If you must use UILabel then you need to subclass it and intercept the touchesBegan/touchesEnded methods (inherited from UIResponder) to detect UIControlEventTouchUpInside yourself.
Problem in OHAttributedLabel. This label also handles tap on links. So for handle tap on any point of label (not just link) you must
self.textLabel.onlyCatchTouchesOnLinks = NO;
Where self.textLabel is your OHAttributedLabel.
And don't forget of userInteractionEnabled.
I don't know if it is the same problem but... I added a label and could not get it to recognize a touch, I eventually realised that it was because I was adding it as a subview, but its frame was outside its parent's frame, hence the touch heirarchy broke
I just had the problem with using static table cells for a settings table where I wanted the whole cell to trigger the first responder for the cell's textfield.
I ended up adding a transparent (custom, blank title) button behind the label (touch disabled) and textfield after not getting any touches using gesture recognizers. I think it should work in a more elegant way, but it solved the task for now and that limited purpose. (and you can just drag connect from the button's default action)
Bit ugly. Then again, it just describes the area behind the text field reacting to touch. Which was the intention after all. So maybe its just not that fancy.
Will keep it until I find the reason for the recognizers not firing.
you can use TTTAttributedLabel to instead it. it's very easy.
when you initial the UITableViewCell,you can delegate:TTTAttributedLabelDelegate
like :
#interface MyTableViewCell : UITableViewCell<TTTAttributedLabelDelegate>{
UILabel *nameLabel;
TTTAttributedLabel *valueLabel;
}
when you initial ,you could add link to label :
[valueLabel addLinkToPhoneNumber:valueStr withRange:NSMakeRange(0, valueStr.length)];
so,you could do anything you want:
- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber{
//do anything you want.
}
I'm making different UIView's tappable (they're not inheriting from UIControl) using the following code:
UITapGestureRecognizer* gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(userTappedOnLink:)];
[labelView setUserInteractionEnabled:YES];
[labelView addGestureRecognizer:gesture];
but I'd also like to change the style when they're highlighted. How do I do that?
Attach UILongPressGestureRecognizer instead of UITapGestureRecognizer to parent view and set it's properties to your liking. The way to track and respond to selection is to implement userTappedOnLink method in appropriate way. This method will be called lots of times in short amount of time when gesture recognizer is activated and you know what's happening by tracking recognizer states.
Implement UIView subclass and create methods, like select and deselect, and customize view properties for each. Then it's only matter of finding which UIView subclass to select or deselect and that's easily done with UIGestureRecognizer method returning point in parent view and iterating trough it's subviews while checking if touch point is inside of particular subview frame.
- (IBAction)userTappedOnLink:(UIGestureRecognizer*)sender
{
switch (sender.state)
{
case UIGestureRecognizerStateBegan:
{
CGPoint touchPoint = [sender locationInView:self.parentView];
for (UIView *subView in [self.parentView subViews)
{
if (CGRectContainsPoint(subView.frame, tapPoint))
{
self.activeSubView = self.subview;
break;
}
}
[self.activeSubView select];
case UIGestureRecognizerStateChanged:[self.activeSubView doNothing];; break;
case UIGestureRecognizerStateEnded:[self.activeSubView deSelect]; self.activeSubView = nil; break;
}
}
There are two ways of handling events in iOS. The first one is to use UIView subclassing and override the methods that are inherited by UIView from UIResponder class (i.e. touchesBegan:withEvent, touchesMoved:withEvent, touchesEnded:withEvent, touchesCancelled:withEvent).
The second way is to create new instance of gesture recognizer class and add it to your UIView object (just like you did) and then create a handler.
- (IBAction)userTappedOnLink:(UIGestureRecognizer *)sender {
// Changing view properties.
}
In both cases you can change the UIView properties. You can find some useful information in "Event handling guide" by Apple. There is a lot of reading but you can have a look only at the "related sample code" (Touches and SimpleGestureRecognizers).
The way you change the style properties of the interface elements in your application depends on what those properties are. Sometimes they can be animated sometimes they're not. Usually the code that changes view properties is placed inside the touchesBegin function or gesture recognizer handler. MoveMe sample code shows how to change views properties and animate them. In the Gesture recognizers chapter of the "Event handling guide" frame properties are changed.
I managed to solve this by adding an UIControl as a subview in the UIView. The UIControl is the same size has a transparent background that changes when it's highlighted. Works like a charm!
well, I have not tested it, just a suggestion, please handle touchesbegin for this view,and call [labelView addGestureRecognizer:gesture]; function.
maybe you should use another function, - (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer .