I have a UIScrollView inside a UIView. For customized paging purpose I have set the clipping of this UIScrollView to No so the UIScrollView still shows up in Region A and Region B of the UIView.
[ region A [UIScrollView] region B ]
Now I want Region A B to be able to trigger scroll events of the UIScrollView's when touched.. I remember there is an one liner (something like [X addGestureRecognizer ...]) that does the trick but I've forgotten what it is... It would be great if someone can tell me what that is!
If you want to redirect the UIScrollView's scrolling gestures to another view, you can do this:
[paddingView addGestureRecognizer:scrollView.panGestureRecognizer];
This won't let you have a fluid control on your scrolling, like the one you get from the UIScrollView.
However, you can use this code :
UISwipeGestureRecognizer *leftSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(viewSwiped:)];
[leftSwipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:leftSwipeGestureRecognizer];
[leftSwipeGestureRecognizer release];
UISwipeGestureRecognizer *rightSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(viewSwiped:)];
[rightSwipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionRight];
[self.view addGestureRecognizer:rightSwipeGestureRecognizer];
[rightSwipeGestureRecognizer release];
firing this method :
- (void)viewSwiped:(UISwipeGestureRecognizer *)swipeGestureRecognizer {
switch (swipeGestureRecognizer.direction) {
case UISwipeGestureRecognizerDirectionLeft:
// Make your scroll view scroll
break;
case UISwipeGestureRecognizerDirectionRight:
// Make your scroll view scroll
break;
default:
// Do Nothing
break;
}
}
It will allow you to retrieve the swipes gestures and page your UIScrollView accordingly.
Two other solutions might be better :
You could use the UIPanGestureRecognizer the same way as above. It recognizes real pan gestures and not only discrete swipes but the implementation of the sync between your finger and the UIScrollView will be slightly more complicated.
You could let your UIScrollView take the total width of the UIView and manage the paging by yourself, recalculating your steps while tracking the current state of the UIScrollView via UIScrollViewDelegate. Again, a bit harder.
Hope this will help,
Related
For the past four hours, I have tried many Stack Overlow solutions but none have helped solve my problem.
Here it is,
I have a UIScrollView
Within that ScrollView there is one custom UILabel and 8 custom UIImageViews
I want to detect a long press
Something like this works
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressDidFire:)];
longPress.minimumPressDuration = 0.5;
[scroll addGestureRecognizer:longPress]; //scroll defined elsewhere
However, if I replace the scroll with any subviews of scroll, the long press event never fires.
How do I detect a long press of a subview of a scroll view?
This is quite a messy hack, however, since I can detect long presses of a scroll view, is there any way where I can detect the
position of the press so that I can see which specific subview is
being pressed.
Also, (insert subview here).userInteractionEnabled = YES, I set this property for all my subviews of the scroll view, so this should not be a problem.
I have also tried using touchesBegan and touchesEnded method as suggested elsewhere in Stack Overflow.
Also, for the image views, I do set a new UILongPressGestureRecognizer for every custom image view, using a for loop, as I am aware of the 1 view per gesture recognizer rule.
From A First Time iOS Developer,
Graham
P.S. I'd really prefer if I could find a solution for 1. rather than the messy 2.
More Code As Requested:
In the Init of the view Controller
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressDidFire:)];
longPress.minimumPressDuration = 0.5;
[self.titleLabel addGestureRecognizer:longPress]; //titleLabel property initialized elsewhere
[mainView addSubview:self.titleLabel];
In a "load images" method
for (NSData * dataImg in results) {
//Does some work turning data into an image, into an image view called img
img.delegate = self;
img.userInteractionEnabled = YES;
UILongPressGestureRecognizer *aLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressDidFire:)];
aLongPress.minimumPressDuration = 0.5;
[img addGestureRecognizer:aLongPress];
[imgContainer addSubview:img];
}
Even More Code + Notes
self.view (UIView)
->scroll (UIScrollView)
->->mainView (UIView)
->->->titleLabel (UILabel)
->->->imgContainer (UIView)
->->->->images (UIImageViews)
[self.view addSubview:scroll];
[scroll addSubview:mainView];
[mainView addSubview:self.titleLabel];
[mainView addSubview:imgContainer];
[imgContainer addSubview:img]; //done 8x via for loop
Thanks to #RegularExpression's answer, I now am aware that the mainView is getting pressed, but not its subviews, so I need to find a way to display the mainView's subviews above it. :)
Another update, titleLabel works. ImageViews still don't work though. :(
I know this is a bit late and an answer has been chosen, but in case someone else wants a nice simple solution if you've got iOS7.
Inside your delegate of the UILongPressGestureRecognizer implement the gestureRecognizer:shouldRequireFailureOfGestureRecognizer:otherGestureRecognizer selector
Check if otherGestureRecognizer is a UIPanGestureRecognizer and return YES, otherwise return NO
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
return YES;
}
return NO;
}
The scroll view will actually produce a UIScrollViewPanGestureRecognizer, which is part of the private API, but it's a subclass of UIPanGestureRecognizer so the above works fine.
To support iOS6 or below, then you'll need to loop through the gestureRecognizers of the UIScrollView, detect which one is a UIPanGestureRecognizer and perform the requireGestureRecognizerToFail selector on your UILongPressGestureRecogizer with that.
your code seems to be fine,it should work i think.i used below code and its working fine for me.
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
longPress.delegate = (id)self;
longPress.minimumPressDuration=0.05;
imageView.userInteractionEnabled = YES;
[imageView addGestureRecognizer:longPress];
and its method,
- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)sender {
NSLog(#"detected");
if (sender.state == UIGestureRecognizerStateEnded){
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"YES" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
}
Here i took imageView as subview of scrollview as u said
Since your UIScrollView is the common parent, that's probably where your gesture recognizer needs to be. You can determine which subview is being pressed by looking at the location of the point supplied in your action. So, the individual subviews do not need gesture recognizers.
So, you would do something like this:
- (void)longPressDidFire:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
CGPoint point = [sender locationInView:scroll];
UIView *tappedView = [scroll hitTest:point withEvent:nil];
So, then you have the view that was long-pressed.
Other things that could cause the action not to fire would be a delegate problem or if the scroll is contained by another view that is intercepting the touch.
HTH
Instead of
[scroll addGestureRecognizer:longPress];
Add the gesture on your subviews, right after you declare them and before you add them to scrollview
[subview addGestureRecognizer:longPress];
Woohoo it works!
The problem was:
imgContainer was a UIView with an empty frame with several UIImageViews as subviews
I was under the impression that as I added a subview to imgContainer, imgContainer would expand.
This is not true.
I had to set imgContainer's frame to the same content frame as the scroll view and then everything became ok.
I hope this answer helps any other future iOS firs timers like me.
I have a scroll view which shows a image view. I am trying to handle UIRotationGestureRecognizer on the image view. I get the event for rotation and apply the required transform on the same. The image gets properly rotated in the scroll view. Then when I do any operations in the scroll view like zoom or pan the image rotation and position goes for a toss
_mainView is the subView of UIScrollView which is also used for zooming
UIRotationGestureRecognizer *rotationGesture=[[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotationGesture:)];
[_mainView addGestureRecognizer:rotationGesture];
[rotationGesture release];
-(void) rotationGesture:(UIRotationGestureRecognizer *) sender {
if(sender.state == UIGestureRecognizerStateBegan ||
sender.state == UIGestureRecognizerStateChanged)
{
sender.view.transform = CGAffineTransformRotate(sender.view.transform,
sender.rotation);
_currRotation = _currRotation + sender.rotation;
[sender setRotation:0];
}
}
I will like to understand what would be the right way to handling rotation with-in the scroll view and it maintains that rotation even after zoom events in Scroll View.
Implement the gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: method in your UIGestureRecognizerDelegate, and return all gestures you want to recognize simultaneously. If you still have trouble with it, check out the answers to UIImageView Gestures (Zoom, Rotate) Question.
Good luck!
EDIT: Your comment has me guessing that the issue is that you can only have one transform on at a time, and the scroll view applies a scale transform, replacing the rotation one. You could remove the native zoom recognizer (see this question), or nest another UIView in the scroll view, and apply the rotation transform to that. I like option two, it seems easier. If you go with option one, use CGAffineTransformConcat to apply both the zoom and rotate transformations independently.
What is the event in iPhone development to write code to a slide left and slide right operation.
As in when you slide the view of your phone, you go into another view. What is the event that i should write my code ?
The event you are looking to catch is called a swipe. Apple provides a UISwipeGestureRecognizer class to handle this for you. Implementation advice can be found in the Gesture Recognizers section of the Event Handling Guide for iOS.
Briefly, you create the recognizer:
UISwipeGestureRecognizer *swipeGestureRecognizer =
[[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(swipe:)] autorelease];
Then set how many fingers the swipe is, and what direction it needs to be in:
swipeGestureRecognizer.numberOfTouchesRequired = 2; // default is 1
// allow swiping either left or right, default is right only
swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft |
UISwipeGestureRecognizerDirectionRight;
And then add it to your view:
[self.view addGestureRecognizer:swipeGestureRecognizer];
When the user swipes, your swipe: method will get called.
Darvids0n covered a gesture to slide the view out. I interpreted the question as an animation to slide the view out, in which case you'll want UIView's transitionFromView:toView:duration:options:completion: class method:
[UIView transitionFromView:view1 toView:view2 duration:1.0 options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromLeft) completion:NULL];
This actually gives you a curl. If you want a slide, you'll have to code a custom animation. Check out UIView's documentation and especially the 'animateWithDuration:animations:' class method.
I've added a TapGestureRecognizer to my self.view:
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
[self.view addGestureRecognizer:tap];
[tap release];
The view contains a single UIScrollView with images and labels. I want to detect if the user taps on a label or not.
- (void)singleTap:(UIGestureRecognizer*)gestureRecognizer {
CGPoint pt = [gestureRecognizer locationInView:self.view];
UIView *v = [self.view hitTest:pt withEvent:nil];
if ([v isKindOfClass:[UILabel class]]) {
NSLog(#"label!");
return;
}
// else do other stuff if its not a label
However I don't see the label! in my log.
I think it's because userInteractionEnabled is by default NO on UILabels. Try turning that on.
EDIT: It was really a guess, but just to confirm, Apple docs on [UIView hitTest:withEvent:] state:
This method ignores view objects that are hidden, that have disabled user interaction, or have an alpha level less than 0.01.
Your subviews, such as the labels themself, actually hide the user interactions from the underlying view.
Why don't you add the gesture recognizers to your label(s).
Alternatively you may want to use UIButton for the labels.
Or -
if you do not want to determine which label has been touched, you may want to add an invisible view (an empty view, neither a hidden one nor one with alpha=0) on top of all labels and add the gesture recognizers to those.
I have a UIView inside of a UIScrollView, both created using IB. The UIView scrolls horizontally inside the UIScrollView. I want to detect left and right 2 finger swipes.
Borrowing from the sample code I found in SmpleGestureRecognizers, I have put the following code in the viewDidLoad method of the UIScrollView's ViewController...
UIGestureRecognizer *recognizer;
UISwipeGestureRecognizer *swipeRightRecognizer, *swipeLeftRecognizer;
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeFrom:)];
swipeRightRecognizer = (UISwipeGestureRecognizer *)recognizer;
swipeRightRecognizer.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:swipeRightRecognizer];
[recognizer release];
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeFrom:)];
swipeLeftRecognizer = (UISwipeGestureRecognizer *)recognizer;
swipeLeftRecognizer.numberOfTouchesRequired = 2;
swipeLeftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeftRecognizer];
[recognizer release];
I have set in the viewcontroller.h. and have the following delegate method...
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
I am assuming this is a valid gestureRecognizer delegate method, but I cannot find any reference to it in the documentation.
I do not get any errors but nothing happens when I do a 2 finger swipe. The delegate method is not called and neither is my action method. I tried removing the numbeOfTouchesRequired call to see if it might work with a single finger swipe to no avail.
Am I adding the gestureRecognizers to the right view? I tried adding it to the UIView, the UIScrollView as well as self.view.superView.
The sample code runs great. The only difference I can see between my implementation of the code and the sample code is the fact that I used IB to create the views and the sample code did not. I suspect that something is consuming the swipe gesture before it gets to my recognizers.
What am I doing wrong.
Thanks,
John
I had the same problem and I solved by using UIPanGestureRecognizer instead of UISwipeGestureRecognizer.
To emulate the detection of swipe, we'll play with the speed of gesture in scrollview.
If the speed of x direction >= 3000 (for example) the swipe will be detected.
If x>0 it will be a right swipe.
The code I implemented to resolve your situation is:
In a uiscrollview named _scroll1:
UIPanGestureRecognizer *pan;
pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(Swipe4ScrollViews:)];
[pan setMinimumNumberOfTouches:2];
[_scroll1 addGestureRecognizer:pan];
[pan release];
With a global BOOL variable named _panning, Swipe4ScrollViews will do the hard job:
-(void)Swipe4ScrollViews:(UIPanGestureRecognizer *)sender
{
if(sender.state == UIGestureRecognizerStateBegan) _panning = NO;
CGPoint v =[sender velocityInView:_scroll1];
NSLog(#"%f, %f",v.x,v.y);
if( (abs(v.x) >= UMBRAL) && !_panning)
{
_panning = YES;
[sender cancelsTouchesInView];
if(v.x>0) NSLog(#"Right");
else NSLog(#"Left");
[self doSomething];
}
}
I encapsulated it on a UIGestureRecognizer subclass: UISwipe4ScrollGestureRecognizer
The biggest difference between the sample code and your code is that your code involves a UIScrollView.
Internally, scroll views, table views, and web views all use gesture recognizers to some degree. If you're expecting to receive gestures within those views – gestures that are similar to the ones already supported internally – they will almost certainly be consumed or significantly delayed before you can get to them. Receiving gestures outside or above those views should work fine, if your use case supports it.
Try setting the delayContentTouches-property of the UIScrollView to NO, maybe it'll help.