UISwipeGestureRecognizer interferes with slider - ios

I have a view in an iOS application (Obj-C) which has an image view in the centre, and immediately below that a slider.
The image view shows album artwork, and the slider can be used to adjust the now-playing track position.
There is also a pair of left and right Swipe Gesture Recognizers. These are used to skip to the next or previous tracks.
The problem is that the swipe gesture recognizers seem to over-ride the users moving the slider thumb.
In my gesture recognizer code I check that the point touched was inside the image view, but it still stops the slider from being moved. (The thumb moves, but jumps back to it's original position when you remove your finger).
This is the code I use to reject the gesture if it's not inside the image view.
- (IBAction)swipeLeftGestureAction:(UISwipeGestureRecognizer *)sender {
// Get the location of the gesture.
CGPoint tapPoint = [sender locationInView:_artworkImageView];
// Make sure tap was INSIDE the artwork image frame.
if( (tapPoint.x <0)
|| (tapPoint.y < 0 )
|| (tapPoint.x > _artworkImageView.frame.size.width)
|| (tapPoint.y>_artworkImageView.frame.size.height))
{
NSLog(#"Outside!");
return;
}
NSLog(#"Swipe LEFT");
[_mediaController skipNext];
}
So my question is, how do I limit the gesture to work ONLY when swiped across the image view?

Try to put the code that restricts the gesture's area in gestureRecognizer:shouldReceiveTouch: and return NO in case you don't want the gesture to receive this touch. It should prevent the gesture from over taking the slider interaction.

If you're only interested in swipes that are inside the image view, then you should add the swipe gesture recognizer to the image view instead of adding it to your entire view. Then you won't need any special logic.

There is a dedicated method for checking if a point is inside a view
BOOL isPointInsideView = [_artworkImageViewpointInside:tapPoint withEvent:nil];
But I think what is happening is that if you will look at the tapPoint is that it actually outside of your imageView
And if your slider is really close to the imageView so the slider captures the movement, what you should do is check on the slider if it is intended for the slider and propagate on to the imageView if needed
Or inherit the UISlider and reduce its response rect

Related

Is there a way to make a UITextField move when user drags across screen?

I'm new to coding so I'm trying some small projects in swift. Right now, I'm trying to make a text box inside the ViewController move when the user drags it along the screen. For the text box, I am currently using a UITextField but I have no idea how to program its movement according to drag.
You'll want to add a UIPanGestureRecognizer to your view. There's all sorts of built in gesture recognizers for detecting various gestures like a tap or in this case a pan (drag). You can check them out here: https://developer.apple.com/documentation/uikit/uigesturerecognizer
Here we'll create a pan gesture recognizer, and add it to our view. Assume myView is your UITextField. A good place to do this is in your view controller's viewDidLoad() method.
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(sender:)))
myView.addGestureRecognizer(pan)
The moment your finger touches the screen, we say that a touch sequence has begun. The touch sequence ends when there are no more fingers on the screen. The pan gesture will determine if this touch sequence looks like a pan, and if so, the method handlePan will be called at various stages. Here, the gesture itself will be passed into the method, which we use to determine translation and move our view accordingly. Add this as a method of your view controller.
#objc func handlePan(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: sender.view)
self.myView.center.x += translation.x
self.myView.center.y += translation.y
sender.setTranslation(CGPoint.zero, in: sender.view)
}
The first line gets the translation in the view which the gesture is attached to (myView). We then adjust myView's position based on this translation, and then we set the translation to zero. This is so that the next time this method is called, the translation will be a delta relative to the previous call.
The property sender.state will tell you the state the gesture is currently in, for example, .began, .changed, .ended. Since a pan is a continuous gesture, our method will be called many times, whenever there's a finger movement.

Swipe down scroll view to dismiss view controller

I use a ImageScrollView from here, which is basically a UIScrollView to allow pinch to zoom into a picture. I now wanted to add the possibility to swipe down the picture to dismiss the view controller. I created a UIPanGestureRecognizer and it works fine if zoom scale is at the minimum value (so the whole picture is visible without zoom). But how can I skip the pan gesture recognizer if the zoom scale is above the minimum value? Because it lays on top of the ImageScrollView, I can't scroll in the picture because scrolling gesture is fetched by the UIPanGestureRecognizer. Any idea how to solve this?
For show image like whatsApp imageView functionality you go with the apple framework QuikLook. It will automatically handle Zoom, Dismiss the image while Swipe etc.
It also support for the documents. It will reduce your effort a lot
It's too easy to handle this operation by adding a trigger on swipe action (gesture calling method).
When zoomScale > minimumValue; set returnstatement
A simple example with Swift 4:
let zoomScale: 1.0
let minimumValue: 0.5
func handlePanGesture(gesture: UIPanGestureRecognizer) {
if (#<set pan gesture down moving condition>#) {
if (zoomScale > minumumValue) {
return
}
}
// perform your next operations
}

How to dismiss programmatically the clipsToBounds property when a GestureRecognizer begin?

I have a UIScrollView which is able to contains many view. To allow a good scrolling (without the content going outside the view while scrolling), on my Main.sotryboard , I've clicked on my UIScrollView and then in the attribute inspector I have allowed the Clip Subviews property:
My problem: all the views which are in my UIScrollViews are draggable (because they all have a UIPanGestureRecognizer.
So, when I try to drag them OUTSIDE my UIScrollView, they just disappear.
In fact they're just going behind every other view
To give you an exemple, I have others components which allow the drop of a view form the precedent UIScrollView. So when I begin the drag'n'drop from it, it disappear, and then reappear in the second component on which I have dropped the view.
What I have tried: I have a special UIPanGestureRecognizer for te drag'n'drop of a view coming from this UIScrollView. So, I actually have this (which, obviously, doesn't work, otherwise I would not be here):
//Here recognizer is the `UIPanGestureRecognizer`
//selectpostit is the name of the view I want to drag
if(recognizer.state == UIGestureRecognizerStateBegan){
selectpostit.clipsToBounds = NO;
}
Any Ideas on how I can improve that?
Thanks in advance.
You could try to reset scrollView.clipsToBounds to NO every time gesture starts, but that would lead to side effect when other content outside scroll view would become visible when dragging is in the progress.
I would recommend to take snapshot of the the draggable view when panning starts, place it on the scrollview's parent, and move it. Such approach should solve your problem.
Here is the code:
- (void)onPanGesture:(UIPanGestureRecognizer*)panRecognizer
{
if(panRecognizer.state == UIGestureRecognizerStateBegan)
{
//when gesture recognizer starts, making snapshot of the draggableView and hiding it
//will move shapshot that's placed on the parent of the scroll view
//that helps to prevent cutting by the scroll view bounds
self.draggableViewSnapshot = [self.draggableView snapshotViewAfterScreenUpdates: NO];
self.draggableView.hidden = YES;
[self.scrollView.superview addSubview: self.draggableViewSnapshot];
}
//your code that updates position of the draggable view
//updating snapshot center, by converting coordinates from draggable view
CGPoint snapshotCenter = [self.draggableView.superview convertPoint:self.draggableView.center toView: self.scrollView.superview];
self.draggableViewSnapshot.center = snapshotCenter;
if(panRecognizer.state == UIGestureRecognizerStateEnded ||
panRecognizer.state == UIGestureRecognizerStateCancelled ||
panRecognizer.state == UIGestureRecognizerStateFailed)
{
//when gesture is over, cleaning up the snapshot
//and showing draggable view back
[self.draggableViewSnapshot removeFromSuperview];
self.draggableViewSnapshot = nil;
self.draggableView.hidden = NO;
}
}
I recommend you look at this article by ray wenderlich Moving Table View Cells with a Long Press Gesture
It explains how to create snapshots

SwipeAction is Triggering with a Delay

I have included a swipe gesture in my app to open a side bar when swiped. I am using XYOrigami to achieve the swipe. Everything is fine but my problem is the swipe action is triggering after a delay. I swipe and when the swipe is over then after few-millisecond its action is triggering. I want the action to follow the finger motion. Can anyone guid me on this.
I hope i am able to explain my question well.
I am adding swipe gesture through xib.
To trigger action along with gesture, we can use pangesture.
But as you said you have a problem that, You have scrollview and you also need to get left or right swipe.
Now I assume,
1. You have vertical scrollview and
2. You want to recongnize left and right gesture.
This can be achieved through velocity.
- (void)panGestureRecognized:(UIPanGestureRecognizer *)recognizer {
CGPoint velocity = [recognizer velocityInView:self.view];
if (velocity.x > 0) {
NSLog(#"To Right");
}
else {
NSLog(#"To Left");
}
}
By checking just x values it will resolve your scrollbar issue.
And You can also check left or right gesture by its positive or negative value.
Refer to this answer for more accurate solution.
https://stackoverflow.com/a/21552885/1101477

How implement longPressGestureRecognizer for subview with disabled user interaction?

I need UI like this:
with 2 buttons (yellow and red) and background view (grey), which will have next behaviour:
- highlight button when i press on it;
- execute when i release on button;
- when i press and move in on button from any other view, button became highlighted (ex: press on grey rect and release on red, or press on yellow and release on red);
- support gestures for buttons (like long press and swipe)
So for solving my problem i found only next way:
I redefine for my GrayView touch methods: touchesCancelled, touchesMoved, touchesBegan, and there are i check if current touch position is belong to some rect - i execute appropriate action. But for this solution i have to make my buttons with userInteractionEnabled = false, which means they doesn't support gestures or other events anymore. So if i what to use support it, i have to implement it by myself, what i don't what to do.
So how can i solve this?
If i understand correctly, you can add the gesture recognizers to the gray view as well. And when the gesture recognizer fires find which colored view was in the touch area:
- (void)tapAction:(UITapGestureRecognizer*)recognizer{
if(recognizer.state == UIGestureRecognizerStateEnded){
CGPoint position = [recognizer locationInView:grayView];
if(CGRectContainsPoint(redView.frame, position) {
...
}
}
}

Resources