简体   繁体   中英

Pan Gesture - Swipe Gesture Conflict

I'm trying to create an application which duplicates the ability of Apple's Photos app (iPhone) to zoom, pan and scroll through photographic images. (I also want to use the same controls when viewing pdfs and other documents.) I got the tap gesture to show/hide the navigation bar and the swipe gesture to scroll through the images from left to right & vice versa. Then I got the pinch gesture to zoom in and out, but when I added the pan gesture to move around within a zoomed image, the swipe gesture quit working.

I found potential solutions elsewhere on StackOverflow including the use of shouldRecognizeSimultaneouslyWithGestureRecognizer , but so far I have not been able to resolve the conflict. Any suggestions?

Here's the code:

func gestureRecognizer(UIPanGestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer UISwipeGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

@IBAction func handlePinch(sender: UIPinchGestureRecognizer) {

    sender.view!.transform = CGAffineTransformScale(sender.view!.transform, sender.scale, sender.scale)
    sender.scale = 1
}

@IBAction func handlePan(sender: UIPanGestureRecognizer) {

    self.view.bringSubviewToFront(sender.view!)
    var translation = sender.translationInView(self.view)
    sender.view!.center = CGPointMake(sender.view!.center.x + translation.x, sender.view!.center.y + translation.y)
    sender.setTranslation(CGPointZero, inView: self.view)
}

@IBAction func handleSwipeRight(sender: UISwipeGestureRecognizer) {

    if (self.index == 0) {
    self.index = ((photos.count) - 1);
    }
    else
    {
    self.index--;
    }

    // requireGestureRecognizerToFail(panGesture)

    setImage()
}

You do not want shouldRecognizeSimultaneouslyWithGestureRecognizer: (which allows two gestures to happen simultaneously). That's useful if you want to, for example, simultaneously pinch and pan. But the simultaneous gestures will not help in this scenario where you are panning and swiping at the same time. (If anything, recognizing those simultaneously probably confuses the situation.)

Instead, you might want to establish precedence of swipe and pan gestures (eg only pan if swipe fails) with requireGestureRecognizerToFail: .

Or better, retire the swipe gesture entirely and use solely the pan gesture, which, if you're zoomed out will be an interactive gesture to navigate from one image to the next, and if zoomed in, pans the image. Interactive pan gestures generally a more satisfying UX, anyway; eg, if swiping from one photo to the next, be able to stop mid pan gesture and go back. If you look at the Photos.app, it's actually using a pan gesture to swipe from one image to another, not a swipe gesture.

I discovered a tutorial at http://www.raywenderlich.com/76436/use-uiscrollview-scroll-zoom-content-swift that does a great job of introducing UIScrollView as a way of combining zooming, panning and paging in Swift. I recommend it for anyone trying to learn how to make these gestures work well together.

In similar case I've used another approach: extended pan gesture to support swipe:

 // in handlePan()
switch recognizer.state {


 struct Holder {
                static var lastTranslate : CGFloat = 0
                static var prevTranslate : CGFloat = 0
                static var lastTime : TimeInterval = 0
                static var prevTime : TimeInterval = 0
            }

        case .began:

            Holder.lastTime = Date.timeIntervalSinceReferenceDate
            Holder.lastTranslate = translation.y
            Holder.prevTime = Holder.lastTime
            Holder.prevTranslate = Holder.lastTranslate


            //perform appropriate pan action

        case .changed:

            Holder.prevTime = Holder.lastTime
            Holder.prevTranslate = Holder.lastTranslate
            Holder.lastTime = Date.timeIntervalSinceReferenceDate
            Holder.lastTranslate = translation.y

            //perform appropriate pan action

        case .ended ,.cancelled:

            let seconds = CGFloat(Date.timeIntervalSinceReferenceDate) - CGFloat(Holder.prevTime)
            var swipeVelocity : CGFloat = 0
            if seconds > 0 {
                swipeVelocity = (translation.y - Holder.prevTranslate)/seconds
            }

            var shouldSwipe : Bool = false
            if Swift.abs(swipeVelocity) > velocityThreshold {
                shouldSwipe = swipeVelocity < 0
            }
            if shouldSwipe {
               // perform swipe action
            } else {
                // perform appropriate pan action
            }

        default:
            print("Unsupported")
        }

All you need to do is to find appropriate velocityTreshold for your swipe gesture

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM