UIImageView捏合缩放Swift

89

我希望有人能够帮助我。我正在尝试让用户在UIImageView上进行捏合缩放(并允许最大和最小级别)。但出于某些原因,它无法正常工作。图像会稍微缩放一下,然后就反弹回来了。谢谢。

这是缩放函数:

func zoom(sender:UIPinchGestureRecognizer) {


    if sender.state == .Ended || sender.state == .Changed {

        let currentScale = self.view.frame.size.width / self.view.bounds.size.width
        var newScale = currentScale*sender.scale

        if newScale < 1 {
            newScale = 1
        }
        if newScale > 9 {
            newScale = 9
        }

        let transform = CGAffineTransformMakeScale(newScale, newScale)

        self.imageView?.transform = transform
        sender.scale = 1

    }

}

13
你可以使用开源的 ImageScrollView,它是一个可以缩放和滚动的图片视图。https://github.com/huynguyencong/ImageScrollView - huync
4
非常感谢!对我来说效果非常好,大约只用了30秒。谢谢。 - 36 By Design
1
@huync,你的ImageScrollView非常好用。谢谢! - Lance Samaria
14个回答

144

使用UIScrollView对UIImageView进行捏合缩放 || 在Swift 3和Xcode 8中实现图像缩放 Youtube视频链接

在storyboard中设置UIScrollView代理 输入图像描述

 class PhotoDetailViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var imgPhoto: UIImageView!
      
    override func viewDidLoad() {
        
        super.viewDidLoad()
    
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 6.0        
        // scrollView.delegate = self - it is set on the storyboard.
    }  
        
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
       
        return imgPhoto
    }

4
太棒了!这篇文章所说的确实就是那么简单! - instanceof
1
这应该是Swift 3的被接受答案。我尝试过其他所有方法都不起作用,包括上面被接受的答案。 - hawmack13
1
不需要在代码中设置滚动视图的代理,因为它已经通过上面的图片在故事板中设置好了。 - C6Silver
我对Swift和Xcode非常陌生,现在我正在尝试在一个包含图片的TableView中完成同样的事情,每个TableCell都有自己的后端。因此,我试图将委托作为每个TableCell的后端,但是它并没有起作用。我按照上述步骤进行了操作,是否还有什么我遗漏的地方吗? - Joel Wahlund
1
如果你是Storyboard类型的,就不需要在代码中设置缩放比例,可以直接在Storyboard的属性检查器中完成。这意味着你甚至不需要为UIScrollView创建一个outlet。 - biomiker
显示剩余5条评论

63

我决定将imageView添加到UIScrollView中,这样用户就可以进行缩放和平移。这是我使用的代码。

为了设置最大/最小缩放比例,我使用了:

    scrollImg.minimumZoomScale = 1.0
    scrollImg.maximumZoomScale = 10.0

以下是剩余的代码。

    var vWidth = self.view.frame.width
    var vHeight = self.view.frame.height

    var scrollImg: UIScrollView = UIScrollView()
    scrollImg.delegate = self
    scrollImg.frame = CGRectMake(0, 0, vWidth!, vHeight!)
    scrollImg.backgroundColor = UIColor(red: 90, green: 90, blue: 90, alpha: 0.90)
    scrollImg.alwaysBounceVertical = false
    scrollImg.alwaysBounceHorizontal = false
    scrollImg.showsVerticalScrollIndicator = true
    scrollImg.flashScrollIndicators()

    scrollImg.minimumZoomScale = 1.0
    scrollImg.maximumZoomScale = 10.0

    defaultView!.addSubview(scrollImg)

    imageView!.layer.cornerRadius = 11.0
    imageView!.clipsToBounds = false
    scrollImg.addSubview(imageView!)

我也必须加上这个。

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

Swift 3及以上版本的函数原型

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

这是我的情况,我需要在UIScrollView中使用UIImageView。到目前为止,它运行得非常好。让我们看看我们的QA会说什么。 - peetadelic

51

支持 Swift 5.1,您可以像这样创建 UIImageView 的扩展:

extension UIImageView {
  func enableZoom() {
    let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(startZooming(_:)))
    isUserInteractionEnabled = true
    addGestureRecognizer(pinchGesture)
  }

  @objc
  private func startZooming(_ sender: UIPinchGestureRecognizer) {
    let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
    guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
    sender.view?.transform = scale
    sender.scale = 1
  }
}

13
喜欢这个解决方案。它可以向左右滚动吗?@John Lima - Naveed Ahmad
3
我发现无论你在哪里捏合缩放,它总是缩放到同一个区域。有没有办法让它在你捏合缩放的任何位置进行缩放? - Robin
2
使用滚动视图可以更好地实现滚动效果。 - John Lima

17

Swift 4 的选项

class ViewController: UIViewController, UIScrollViewDelegate {

@IBOutlet weak var scrolView: UIScrollView!
@IBOutlet weak var imgPhoto: UIImageView!

  override func viewDidLoad() {
    super.viewDidLoad()
    scrolView.delegate = self
    scrolView.minimumZoomScale = 1.0
    scrolView.maximumZoomScale = 10.0
  }

  func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imgPhoto
  }
}

2
当缩放结束时,调用 scrollView.zoomScale = 1.0 - Marco Leong
解决方案已经就绪,谢谢。 - Naveed Ahmad

15
您可以使用开源的ImageScrollView,它是一个可缩放和可滚动的图像视图。http://github.com/huynguyencong/ImageScrollView 与此类似的开源项目还可以在ScrollView中添加ImageView。
open class ImageScrollView: UIScrollView {
   var zoomView: UIImageView? = nil
}

extension ImageScrollView: UIScrollViewDelegate{

    public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return zoomView
    }

    public func scrollViewDidZoom(_ scrollView: UIScrollView) {
        adjustFrameToCenter()
    }
}

1
我认为这是最好的答案。它可以缩放和滚动,并且还支持双击操作。 - mazend
2
我想再说一件事。它非常容易使用。 - mazend
1
这种方法对我来说效果最好。 - Carl Smith

9
使用Swift 5.0,这是我的工作方式:
let myImageView = UIImageView(image: myImage)
myImageView.isUserInteractionEnabled = true
let pinchMethod = UIPinchGestureRecognizer(target: self, action: #selector(pinchImage(sender:)))
myImageView.addGestureRecognizer(pinchMethod)

@objc func pinchImage(sender: UIPinchGestureRecognizer) {
  guard let sender = sender.view else { return }

if let scale = (sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)) {
  guard scale.a > 1.0 else { return }
  guard scale.d > 1.0 else { return }
  sender.view?.transform = scale
  sender.scale = 1.0
 }
}

您可以使用scale.a、scale.b、scale.c、scale.d、scale.tx和scale.ty来设置比例限制。


6

在我看来,问题出在你对currentScale的确定上。它总是等于1,因为你改变了imageView的缩放比例。你应该按照以下方式分配你的currentScale:

let currentScale = self.imageView?.frame.size.width / self.imageView?.bounds.size.width  

6

Swift 3解决方案

默认情况下,UIImageViewuserInteration被禁用。在添加任何手势之前,请先启用它。

imgView.isUserInteractionEnabled = true

与屏幕坐标中两个触摸点相关的比例因子

var lastScale:CGFloat!
func zoom(gesture:UIPinchGestureRecognizer) {
    if(gesture.state == .began) {
        // Reset the last scale, necessary if there are multiple objects with different scales
        lastScale = gesture.scale
    }
    if (gesture.state == .began || gesture.state == .changed) {
    let currentScale = gesture.view!.layer.value(forKeyPath:"transform.scale")! as! CGFloat
    // Constants to adjust the max/min values of zoom
    let kMaxScale:CGFloat = 2.0
    let kMinScale:CGFloat = 1.0
    var newScale = 1 -  (lastScale - gesture.scale)
    newScale = min(newScale, kMaxScale / currentScale)
    newScale = max(newScale, kMinScale / currentScale)
    let transform = (gesture.view?.transform)!.scaledBy(x: newScale, y: newScale);
    gesture.view?.transform = transform
    lastScale = gesture.scale  // Store the previous scale factor for the next pinch gesture call
  }
}

2
尝试清楚地解释一下:lastScale是什么,这里的new scale是什么,你在哪里声明它们,它们的子类是什么。 - srivas
请问您能否在这里添加更多细节?代码不完整。lastScale是什么,new scale是什么? - aBilal17
如何在结束缩放后返回原始大小 @rajeshkumar-r - reza_khalafi

5

Swift 3解决方案

这是我使用的代码。我将imageView作为子视图添加到scrollView中。

class ZoomViewController: UIViewController,UIScrollViewDelegate {

@IBOutlet weak var scrollView:UIScrollView!
@IBOutlet weak var imageView:UIImageView!

override func viewDidLoad() {

        super.viewDidLoad()
        scrollView.delegate = self

        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 10.0//maximum zoom scale you want
        scrollView.zoomScale = 1.0

}

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

2

[2022年9月,Swift 5]

在我看来,一个有效的方法是将图像视图嵌入到可滚动视图中。为了让答案简短明了,您可以查看我分享的库以更好地理解。我已经建立了这个小型库并在生产应用程序中使用它。您可以安装或只需将文件复制粘贴到您的项目中。它非常易于作为UIView使用,并通过监听其委托来获取更多选项。

该库名为InteractiveImageView,支持iOS 11.0及以上版本,链接到GitHub: https://github.com/egzonpllana/InteractiveImageView


好的,我很快就会尝试。那么这个库在生产环境中稳定吗? - sabiland
1
是的,它像所有好的一样工作! - Egzon P.
1
太好了,我会用你的库替换当前的ImageScrollView库。切换视图选项正是我在当前库中所缺少的。 - sabiland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接