简体   繁体   中英

How to forward the pan gesture from UIScrollView to UIImageView?

I have a UIScrollView and inside a UIImageView so that I can pinche zoom the image view using:

extension CropperViewController : UIScrollViewDelegate {

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return self.imageView;
    }
}

I now also want to be able to freely move the UIImageView so I tried adding a UIPanGestureRecognizer to my UIImageView`:

self.imageView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:))));

func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
    if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
        let translation = gestureRecognizer.translation(in: self.view);

        gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y);
        gestureRecognizer.setTranslation(CGPoint.zero, in: self.view);
    }
}

I now had the problem that no pan touch event was fired at all so I thought maybe the UIScrollView is catching all those events. So some research on Stackoverflow told me to add the following to my UIScrollView :

let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)));
tapGestureRecognizer.numberOfTapsRequired = 1;
tapGestureRecognizer.cancelsTouchesInView = false;

self.scrollView.addGestureRecognizer(tapGestureRecognizer);

but actually that changed nothing. I can zoom and move the image after zooming but I can not move the image using UIPanGestureRecognizer . I want to use the UIScrollView to be able to zoom but I want to use the UIPanGestureRecognizer to move the UIImageView .

How can I do that?

EDIT

Maybe it is possible to disable or change the pan gesture recognizer of the UIScrollView and forward those events to the UIImageView ?

You need to give the pan recognizer a delegate, then return true for shouldRecogniseSimultaneouslyWith....

You may also need to do the same with the scroll view's pan recognizer, which is available as a property.

Alternatively, add another target/action to the scroll view's pan recognizer (using addTarget(_, action:) instead of creating your own.

Try this:

scrollView.panGestureRecognizer.require(toFail: imagePanRecognizer)

If you still get problems (like scrolling feeling laggy) set scrollView's delaysContentTouches to false

Since it seems that there isnt a solution for that I came up with not using a UIScrollView and impelementing UIPinchGestureRecognizer and UIPanGestureRecognizer for my UIImageView by myself:

import Foundation
import UIKit

/**
 *
 */
protocol CropperCallback {

    /**
     *
     */
    func croppingDone(image: UIImage);

    /**
     *
     */
    func croppingCancelled();
}

/**
 *
 */
class CropperViewController : UIViewController {
    /**
     *
     */
    @IBOutlet var imageView: UIImageView!;
    /**
     *
     */
    var imageViewScaleCurrent: CGFloat! = 1.0;
    var imageViewScaleMin: CGFloat! = 0.5;
    var imageViewScaleMax: CGFloat! = 5.0;
    /**
     *
     */
    @IBOutlet var cropAreaView: CropAreaView!;
    /**
     *
     */
    @IBOutlet weak var cropAreaViewConstraintWidth: NSLayoutConstraint!
    @IBOutlet weak var cropAreaViewConstraintHeight: NSLayoutConstraint!
    /**
     *
     */
    @IBOutlet var btnCrop: UIButton!;
    /**
     *
     */
    @IBOutlet var btnCancel: UIButton!;

    /**
     *
     */
    var callback: CropperCallback! = nil;
    /**
     *
     */
    var image: UIImage! = nil;
    /**
     *
     */
    var imageOriginalWidth: CGFloat!;
    var imageOriginalHeight: CGFloat!;
    /**
     *
     */
    var cropWidth: CGFloat! = 287;
    /**
     *
     */
    var cropHeight: CGFloat! = 292;
    /**
     *
     */
    var cropHeightFix: CGFloat! = 1.0;
    /**
     *
     */
    var cropArea: CGRect {

        /**
         *
         */
        get {
            let factor = self.imageView.image!.size.width / self.view.frame.width;
            let scale = 1 / self.imageViewScaleCurrent;
            let x = (self.cropAreaView.frame.origin.x - self.imageView.frame.origin.x) * scale * factor;
            let y = (self.cropAreaView.frame.origin.y - self.imageView.frame.origin.y) * scale * factor;
            let width = self.cropAreaView.frame.size.width * scale * factor;
            let height = self.cropAreaView.frame.size.height * scale * factor;

            return CGRect(x: x, y: y, width: width, height: height);
        }
    }

    /**
     *
     */
    static func storyboardInstance() -> CropperViewController? {
        let storyboard = UIStoryboard(name: String(describing:  NSStringFromClass(CropperViewController.classForCoder()).components(separatedBy: ".").last!), bundle: nil);

        return storyboard.instantiateInitialViewController() as? CropperViewController;
    }

    /**
     *
     */
    override func viewDidLoad() {
        super.viewDidLoad();

        self.imageView.image = self.image;
        self.imageView.isUserInteractionEnabled = true;
        self.imageView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:))));
        self.imageView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(self.handlePinch(_:))));

        self.cropAreaViewConstraintWidth.constant = self.cropWidth;
        self.cropAreaViewConstraintHeight.constant = self.cropHeight;
        self.cropAreaView.layer.borderWidth = 1;
        self.cropAreaView.layer.borderColor = UIColor(red: 173/255, green: 192/255, blue: 4/255, alpha: 1.0).cgColor;

        self.btnCrop.addTarget(self, action: #selector(self.didTapCropButton), for: UIControlEvents.touchUpInside);
        self.btnCancel.addTarget(self, action: #selector(self.didTapCancelButton), for: UIControlEvents.touchUpInside);
    }

    /**
     *
     */
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews();

        let imageOriginalRect = self.getRectOfImageInImageView(imageView: self.imageView);

        self.imageOriginalWidth = imageOriginalRect.size.width;
        self.imageOriginalHeight = imageOriginalRect.size.height;
    }

    /**
     *
     */
    func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
        if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
            let rect = self.getRectOfImageInImageView(imageView: self.imageView);
            let xImage = rect.origin.x;
            let yImage = rect.origin.y;
            let widthImage = rect.size.width;
            let heightImage = rect.size.height;

            let xCropView = self.cropAreaView.frame.origin.x;
            let yCropView = self.cropAreaView.frame.origin.y;
            let widthCropView = self.cropAreaView.frame.size.width;
            let heightCropView = self.cropAreaView.frame.size.height;

            let translation = gestureRecognizer.translation(in: self.view);

            var x: CGFloat;
            var y: CGFloat;

            if (translation.x > 0) {
                if (!(xImage >= xCropView)) {
                    x = gestureRecognizer.view!.center.x + translation.x;
                } else {
                    x = gestureRecognizer.view!.center.x;
                }
            } else if (translation.x < 0) {
                if (!((xImage + widthImage) <= (xCropView + widthCropView))) {
                    x = gestureRecognizer.view!.center.x + translation.x;
                } else {
                    x = gestureRecognizer.view!.center.x;
                }
            } else {
                x = gestureRecognizer.view!.center.x;
            }

            if (translation.y > 0) {
                if (!(yImage >= (yCropView - self.cropHeightFix))) {
                    y = gestureRecognizer.view!.center.y + translation.y;
                } else {
                    y = gestureRecognizer.view!.center.y;
                }
            } else if (translation.y < 0) {
                if (!((yImage + heightImage) <= (yCropView + heightCropView + self.cropHeightFix))) {
                    y = gestureRecognizer.view!.center.y + translation.y;
                } else {
                    y = gestureRecognizer.view!.center.y;
                }
            } else {
                y = gestureRecognizer.view!.center.y;
            }

            gestureRecognizer.view!.center = CGPoint(x: x, y: y);
            gestureRecognizer.setTranslation(CGPoint.zero, in: self.view);

            self.fixImageViewPosition();
        }
    }

    /**
     *
     */
    func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
        if let view = gestureRecognizer.view {
            let widthCropView = self.cropAreaView.frame.size.width;
            let heightCropView = self.cropAreaView.frame.size.height;

            if (((self.imageViewScaleCurrent * gestureRecognizer.scale * self.imageOriginalWidth) > widthCropView)
                && ((self.imageViewScaleCurrent * gestureRecognizer.scale * self.imageOriginalHeight) > (heightCropView + (2 * self.cropHeightFix)))
                && ((self.imageViewScaleCurrent * gestureRecognizer.scale) < self.imageViewScaleMax)) {

                self.imageViewScaleCurrent = self.imageViewScaleCurrent * gestureRecognizer.scale;

                view.transform = CGAffineTransform(scaleX: self.imageViewScaleCurrent, y: self.imageViewScaleCurrent);
            }

            gestureRecognizer.scale = 1.0;

            self.fixImageViewPosition();
        }
    }

    /**
     *
     */
    func fixImageViewPosition() {
        let rect = self.getRectOfImageInImageView(imageView: self.imageView);
        let xImage = rect.origin.x;
        let yImage = rect.origin.y;
        let widthImage = rect.size.width;
        let heightImage = rect.size.height;

        let xCropView = self.cropAreaView.frame.origin.x;
        let yCropView = self.cropAreaView.frame.origin.y;
        let widthCropView = self.cropAreaView.frame.size.width;
        let heightCropView = self.cropAreaView.frame.size.height;

        if (xImage > xCropView) {
            self.imageView.frame = CGRect(x: xCropView, y: self.imageView.frame.origin.y, width: widthImage, height: heightImage);
        }

        if ((xImage + widthImage) < (xCropView + widthCropView)) {
            self.imageView.frame = CGRect(x: ((xCropView + widthCropView) - widthImage), y: self.imageView.frame.origin.y, width: widthImage, height: heightImage);
        }

        if (yImage > yCropView) {
            self.imageView.frame = CGRect(x: self.imageView.frame.origin.x, y: (yCropView - self.cropHeightFix), width: widthImage, height: heightImage);
        }

        if ((yImage + heightImage) < (yCropView + heightCropView + self.cropHeightFix)) {
            self.imageView.frame = CGRect(x: self.imageView.frame.origin.x, y: ((yCropView + heightCropView + self.cropHeightFix) - heightImage), width: widthImage, height: heightImage);
        }
    }

    /**
     *
     */
    func getRectOfImageInImageView(imageView: UIImageView) -> CGRect {
        let imageViewSize = imageView.frame.size;
        let imageSize = imageView.image!.size;

        let scaleW = imageViewSize.width / imageSize.width;
        let scaleH = imageViewSize.height / imageSize.height;
        let aspect = min(scaleW, scaleH);

        var imageRect = CGRect(x: 0, y: 0, width: (imageSize.width * aspect), height: (imageSize.height * aspect));

        imageRect.origin.x = (imageViewSize.width - imageRect.size.width) / 2;
        imageRect.origin.y = (imageViewSize.height - imageRect.size.height) / 2;

        imageRect.origin.x += imageView.frame.origin.x;
        imageRect.origin.y += imageView.frame.origin.y;

        return imageRect;
    }

    /**
     *
     */
    func didTapCropButton(sender: AnyObject) {
        let croppedCGImage = self.imageView.image?.cgImage?.cropping(to: self.cropArea);
        let croppedImage = UIImage(cgImage: croppedCGImage!);

        if (self.callback != nil) {
            self.callback.croppingDone(image: croppedImage);
        }

        self.dismiss(animated: true, completion: nil);
    }

    /**
     *
     */
    func didTapCancelButton(sender: AnyObject) {
        if (self.callback != nil) {
            self.callback.croppingCancelled();
        }

        self.dismiss(animated: true, completion: nil);
    }
}

/**
 *
 */
extension UIImageView {

    /**
     *
     */
    func imageFrame() -> CGRect {
        let imageViewSize = self.frame.size;

        guard let imageSize = self.image?.size else {
            return CGRect.zero;
        }

        let imageRatio = imageSize.width / imageSize.height;
        let imageViewRatio = imageViewSize.width / imageViewSize.height;

        if (imageRatio < imageViewRatio) {
            let scaleFactor = imageViewSize.height / imageSize.height;
            let width = imageSize.width * scaleFactor;
            let topLeftX = (imageViewSize.width - width) * 0.5;

            return CGRect(x: topLeftX, y: 0, width: width, height: imageViewSize.height);
        } else {
            let scaleFactor = imageViewSize.width / imageSize.width;
            let height = imageSize.height * scaleFactor;
            let topLeftY = (imageViewSize.height - height) * 0.5;

            return CGRect(x: 0, y: topLeftY, width: imageViewSize.width, height: height);
        }
    }
}

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