I have a scrollview through which the user can swipe left and right to navigate and tap on the elements in it, but I also want to add the ability to swipe up or down on the scrollview (perpendicular to the scrolling direction) to perform some other actions. However, when I tried to add the gesture recognizers to it, it didn't seem to make a difference. I then tried layering another view on top of it and adding the gesture recognizers to that, the gesture recognizers worked, but I couldn't figure out how to pass the rest of the touches down through it. What's the best approach here?
Set the delegate for scrollview you have added,
#property (nonatomic, assign) NSInteger lastContentOffset
//
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
if (self.lastContentOffset > scrollView.contentOffset.y)
// down direction
else if (self.lastContentOffset < scrollView.contentOffset.y)
// up direction
self.lastContentOffset = scrollView.contentOffset.y;
// do whatever you need to with scrollDirection here.
}
You can get help from this link also.
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 am having an horizontal scrollview in an UIViewController, where i have many images in small sizes. I am keeping the images in scrollview because the images are more, so user can scroll horizontally and choose images. But, the problem is, i have to select an image and drag and drop to that UIViewController view. But, since the images are in scrollview, drag and drop images into UIViewcontroller's view is not working, not detecting the touch events too.
Please NOTE: If i don't have scrollview but just keeping the images also into UIViewcontroller's view itself, drag and drop the images on the same screen, is working very well.
How can I resolve this when I need to have scrollview and drag and drop images, any advice/help please?
Hi Getsy,
I am not going to provide you code directly but give idea how to manage this.
You can manage this way, When you get touch on your object in scrollView at that time or when you move that object by draging at that time disable scroll by myScroll.scrollEnabled = NO;
Then When on endTouch you can enable Scroll by myScroll.scrollEnabled = YES; So by this you can manage you object moving in scroll hope you got logic.
Here is the demo code : Drag and Drop with ScrollView. which has same logic of Disabling scroll view on touchesMoved: and Enabling scroll view on touchesEnded:.
I did implement that behaviour before without any subclassing.
I used canCancelContentTouches = NO of the UIScrollView to make sure the subviews handle there touches on their own. If a subview (in your case an image) was touched, i moved the view out of the scrollview onto the superview and started tracking it's dragging. (You have to calculate the correct coordinates within the new superview, so it stays in place).
After dragging finishes, i checked if the target area was reached, otherwise I moved it back into the scrollview. If that's not detailed enough I could post some code.
Well here is my example code: Github: JDDroppableView
Getsy,
Try the code for drag and drop the objects :
-(void)dragAndDropWithGesture {
UILongPressGestureRecognizer *downwardGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(dragGestureChanged:)];
[scrollViewAlfabeto addGestureRecognizer:downwardGesture];
for (UIGestureRecognizer *gestureRecognizer in myscrollView.gestureRecognizers)
{
[gestureRecognizer requireGestureRecognizerToFail:downwardGesture];
}
}
- (void) dragGestureChanged:(UIPanGestureRecognizer*)gesture
{
CGPoint point = [gesture locationInView:scrollViewAlfabeto];
if (gesture.state == UIGestureRecognizerStateBegan)
{
[imageViewToMove removeFromSuperview];
[self.view addSubview:imageViewToMove];
UIView *draggedView = [myscrollView hitTest:point withEvent:nil];
if ([draggedView isKindOfClass:[UIImageView class]])
{
imageViewToMove = (UIImageView*)draggedView;
}
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
imageToMove.center = point;
}
else if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateFailed)
{
// Determine if dragged view is in an OK drop zone
// If so, then do the drop action, if not, return it to original location
NSLog(#"point.x final:%f", point.x);
NSLog(#"point.y final:%f", point.y);
if (CGRectContainsPoint(goal.frame, point)){
imageToMove.frame = CGRectMake(167, 159, 100, 100);
}
else{
[imageToMove removeFromSuperview];
[myscrollView addSubview:imageToMove];
[imageToMove setFrame:CGRectMake(12, 38, 100, 100)];
imageToMove = nil;
}
}
}
May this code will help you out.
For a similar problem, I made UIScrollView subclass like the following: PoliteScrollView passes touch messages to it's subviews when it determines that they are being dragged.
#interface PoliteScrollView : UIScrollView
// number of pixels perpendicular to the scroll views orientation to make a drag start counting as a subview drag
// defaults to 1/10 of the scroll view's width or height, whichever is smaller
#property(nonatomic,assign) CGFloat subviewDraggingThreshold;
// yes when a subview is being dragged
#property(nonatomic,assign) BOOL isDraggingSubview;
// the subview being dragged
#property(nonatomic,strong) UIView *draggingSubview;
#end
#protocol PoliteScrollViewDelegate <NSObject>
#optional
- (void)scrollView:(PoliteScrollView *)scrollView subviewTouchesBegan:(NSSet *)touches;
- (void)scrollView:(PoliteScrollView *)scrollView subviewTouchesMoved:(NSSet *)touches;
- (void)scrollView:(PoliteScrollView *)scrollView subviewTouchesEnded:(NSSet *)touches;
#end
The key design idea is that dragging in a scroll view is ambiguous. Is the user scrolling or dragging a subview? PoliteScroll view handles this by providing two things: (1) a notion of orientation (horizontal if it's longer than it is wide, vertical otherwise), and (2) a threshold distance for what constitutes a drag in the direction perpendicular to it's orientation. (defaults to 1/10 the width or height).
I pasted this and several other files are in a paste bin, containing the following:
PoliteScrollView .h and .m
DraggableImageView.h and .m - that changes it's position when it gets touch messages.
ViewController.m - that demonstrates thetwo in combination.
To combine these in a project, paste the paste bin into files appropriately named, add a storyboard with a PoliteScrollView (be sure to set it's delegate), add in some images (the ViewController tries to add puppy0.jpeg to puppy4.jpeg.
I created an example which illustrates how to drag and drop between two or more views:
http://www.ancientprogramming.com/2012/04/05/drag-and-drop-between-multiple-uiviews-in-ios/
I found it a good idea to register the gesture recognizer to a different view than the actual views being dragged. This will make sure the gestures continue even though the dragged view changes its 'parent' view.
Maybe it can give some inspiration
I'm looking at some existing code that adds a pinchGestureRecognizer at a specific zoom level of a scrollView. (Like when the scrollView.zoomScale > 10). At this zoom level, the pinch gesture does some special handling with a (handlePinch: selector)on the scrollView on the pinch.
I'm tasked to have a slider emulate the zooming of the scrollView so the user doesn't have to use two fingers to pinch all the time to zoom. I'd like to add the pinchGesture when my zoomScale is > 10 for the scrollView to get the same special handling. I don't want to add two of the same gestureRecognizers since I am assuming that if I blindly add it when the zoomScale > 10, I'm going to get unwanted behavior. I don't know how to check for a specific gesture in this case.
I basically want to do something like this:
- (IBAction)sliderChanged:(id)sender {
UISlider *slider = (UISlider *)sender;
if (slider.value > .6 && slider.value < .8) {
// check for pinch gesture
// I thought I could get the NSArray of gestures from my self.scrollView and check if it's empty, but there are other gestures are already attached to the scrollView.
// I thought I could also try self.scrollView respondsToSelector:#selector(handlePinch:), but I don't think that works.
//self.scrollView addGestureRecognizer
}
if (yourPinchRecognizer == nil) {
// do something
}
This has worked for me with the app I am working on.
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 am looking for a way to create a scrollViewIsZooming method that is called while zooming is occuring. Does anyone know of a way to do this?
I am wanting to use it to keep the content centered in the scrollView while zooming. If I use the scrollViewDidEndZooming method, the content snaps back to the center after zooming is finished.
Thanks!
There's a zooming propriety on UIScrollView :
#property(nonatomic, readonly, getter=isZooming) BOOL zooming
If you check it in each
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
call it should work.
What about viewForZoomingInScrollView:
If you put a pinch gesture recogniser in the view then connect it to an action you can control zooming that way. I have an action "doPinch" and an outlet "pinchRecognizer" that I use like this:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.zoomableImage;
}
Then
- (IBAction)doPinch:(id)sender
{
NSLog(#"In the pinch action now with scale: %f", self.pinchRecognizer.scale);
[scrollView setZoomScale:self.pinchRecognizer.scale animated:NO];
}
Might be easier. Note that scrollView is my outlet connected to the scroll view.