Dùng UIScrollView để zoom ảnh

I. Giới thiệu

Bạn muốn tạo một playlist các ảnh có size khác nhau và zoom in zoom out chúng, có 2 cách để làm được như vậy:

Cách thứ nhất là dùng pinch gesture Cách thứ hai dùng UIScrollView Trong bài viết này, mình sẽ dùng UIScrollView để thực hiện Bắt đầu nhé!

II. Nội dung

Việc thực hiện gồm 3 bước sau:

  1. Trong interface của viewcontroller, kéo 1 UIScrollView vào sau đó kéo 1 UIImageView làm subView của UIScrollView. Set delegate của scrollView là viewController.

  2. Kéo outlet cho scrollView và imageView vào viewController với tên tương ứng

  3. Xử lý frame cho UIScrollView và UIImageView. Đây là bước quan trọng nhất. Bản chất như sau:

    a. Tạo frame cho ScrollView để nó vừa đúng bằng view của viewController bằnng cách set autoResizeMask như sau:

    Screen Shot 2016-10-28 at 12.07.58 PM.png

    b. Về phía UIImageView, có nhiều việc phải làm hơn:

    • Gán size của UIImageView bằng đúng size image của nó. Thực hiện bước này sẽ khiến cho ảnh của ImageView vừa khít, ko bị thừa 2 bên lề hay bóp méo

    • Đưa imageView ở giữa màn hình bằng cách gán: imageView.center = view.center

         private func updateSizeForImageView(size: CGSize) {
            imageView.center = view.center
            imageView.frame.size = CGSizeMake(size.width, size.height)
        }

c. Tại thời điểm này, imageView đã có size vừa đúng bằng size của image(ảnh không thừa không thiếu), và ở giữa màn hình. Tuy nhiên, chúng ta cần nhiều hơn thế. Chúng ta muốn ảnh sẽ có kích thước vừa màn hình nhất có thể(tối thiểu chiều dài hoặc chiều rộng phải bằng chiều dài và chiều rộng của màn hình) nhưng vẫn giữ nguyên tỷ lệ, vì vậy cần tính tỷ lệ zoom của scrollView như sau:

        private func updateMinZoomScaleForSize(size: CGSize) {
            let widthScale = size.width / imageView.bounds.width
            let heightScale = size.height / imageView.bounds.height
            let minScale = min(widthScale, heightScale)
            scrollView.minimumZoomScale = minScale
            scrollView.zoomScale = minScale
        }
d. Đến đây, imageView đã vừa đúng màn hình và nhìn đã rất đẹp rồi. Tuy nhiên, khi chúng ta zoom in, size của UIImageView thay đổi, nó sẽ không còn ở giữa màn hình nữa. Chúng ta cần phải đảm bảo kể cả khi zoom, ảnh vẫn luôn ở giữa màn hình bằng cách implement 2 method của UIScrollViewDelegate:

- Cho scrollView biết đối tượng nó cần zoom là imageView:
        func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
            return imageView
        }
- Khi zoom thực hiện tính lại origin cho imageView sao cho nó luôn ở giữa màn hình:
        private func updateConstraintsForSize(size: CGSize) {
            let realHeight = imageView.bounds.height * scrollView.zoomScale
            let readWidth = imageView.bounds.width * scrollView.zoomScale
            let deltaHeight = view.bounds.height - realHeight
            let deltaWidth = view.bounds.width - readWidth
            let yOffset = max(0, deltaHeight/2)
            let xOffset = max(0, deltaWidth/2)
            imageView.frame.origin = CGPointMake(xOffset, yOffset)
        }

e. Set backgroundColor của view là blackColor(), của scrollView và imageView là clearColor để nhìn cool hơn, sau đó nhấn Build và enjoy:D

III. Tổng kết

UIScrollView ngoài việc được biết như một super class của 2 class vô cùng quen thuộc là UITableView và UICollectionView thì còn có nhiều tính năng mới rất hữu ích và việc xử lý zoom ảnh trong bài viết này chỉ là một trong số đó. Hy vọng bài viết này sẽ giúp ích được cho các bạn trong hành trình trở thành một iOS developer chuyên nghiệp:D. Happy coding!

Toàn bộ source code:

import UIKit

class ViewController: UIViewController {
    @ IBOutlet weak var scrollView: UIScrollView!
    @ IBOutlet weak var imageView: UIImageView!
    var image = UIImage(named: "lion.jpg")
    override func viewDidLoad() {
        super.viewDidLoad()
        updateSizeForImageView(image!.size)
        updateMinZoomScaleForSize(view.bounds.size)
        imageView.image = image
    }

    private func updateSizeForImageView(size: CGSize) {
        imageView.center = view.center
        imageView.frame.size = CGSizeMake(size.width, size.height)
    }

    private func updateMinZoomScaleForSize(size: CGSize) {
        let widthScale = size.width / imageView.bounds.width
        let heightScale = size.height / imageView.bounds.height
        let minScale = min(widthScale, heightScale)
        scrollView.minimumZoomScale = minScale
        scrollView.zoomScale = minScale
    }

    private func updateConstraintsForSize(size: CGSize) {
        let realHeight = imageView.bounds.height * scrollView.zoomScale
        let readWidth = imageView.bounds.width * scrollView.zoomScale
        let deltaHeight = view.bounds.height - realHeight
        let deltaWidth = view.bounds.width - readWidth
        let yOffset = max(0, deltaHeight/2)
        let xOffset = max(0, deltaWidth/2)
        imageView.frame.origin = CGPointMake(xOffset, yOffset)
    }
}
extension ViewController: UIScrollViewDelegate {
    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    func scrollViewDidZoom(scrollView: UIScrollView) {
        updateConstraintsForSize(view.bounds.size)
    }
}