스크롤 뷰에 이미지 스케일 적용 및 Zoom In/Out 기능

//
//  ImageConfirmViewController.swift
//  Created by DongOh Lim on 2023/06/15.
//

import UIKit

class ImageConfirmViewController: UIViewController {
    
    var image: UIImage?
    var naviBar: UINavigationBar?
    var complete: ((Bool) -> Void)?
    
    let imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        return imageView
    }()
    
    var stackView: UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .horizontal
        stackView.alignment = .fill
        stackView.distribution = .fillEqually
        stackView.spacing = 5
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()
    
    var okButton: UIButton = {
        let button = UIButton()
        button.setTitle("확인", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = UIColor.hexStringToUIColor(hex: "#3C9FFF")
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    var cancelButton: UIButton = {
        let button = UIButton()
        button.setTitle("취소", for: .normal)
        button.setTitleColor(.lightGray, for: .normal)
        button.layer.borderWidth = 2.0
        button.layer.borderColor = UIColor.lightGray.cgColor
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        // navigationBar 추가
        var statusBarHeight: CGFloat = 0
        statusBarHeight = UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0
        
        naviBar = UINavigationBar(frame: .init(x: 0, y: statusBarHeight, width: view.frame.width, height: statusBarHeight))
        naviBar?.isTranslucent = false
        naviBar?.backgroundColor = .white
        
        let naviItem = UINavigationItem(title: "이미지확인")
        naviBar?.items = [naviItem]
        view.addSubview(naviBar!)
        
        setup()
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        okButton.layer.cornerRadius = okButton.frame.height / 2
        cancelButton.layer.cornerRadius = cancelButton.frame.height / 2
    }
    
    func setup() {
        okButton.addTarget(self, action: #selector(okButton(_:)), for: .touchUpInside)
        cancelButton.addTarget(self, action: #selector(cancelButton(_:)), for: .touchUpInside)
        
        // 스텍뷰 버튼 추가
        stackView.addArrangedSubview(okButton)
        stackView.addArrangedSubview(cancelButton)
        
        // UIStackView Constraints
        view.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            stackView.heightAnchor.constraint(equalToConstant: 50),
        ])
        
        // 줌 In/Out 스크롤 뷰
        let zoomView: UIScrollView = {
            let scrollView = UIScrollView()
            scrollView.delegate = self
            scrollView.zoomScale = 1.0
            scrollView.minimumZoomScale = 1.0
            scrollView.maximumZoomScale = 3.0
            scrollView.isScrollEnabled = true
            scrollView.showsVerticalScrollIndicator = false
            scrollView.showsHorizontalScrollIndicator = false
            scrollView.clipsToBounds = true
            scrollView.translatesAutoresizingMaskIntoConstraints = false
            return scrollView
        }()
        
        // UIScrollView Constraints
        view.addSubview(zoomView)
        NSLayoutConstraint.activate([
            zoomView.topAnchor.constraint(equalTo: self.naviBar!.bottomAnchor),
            zoomView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            zoomView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            zoomView.bottomAnchor.constraint(equalTo: stackView.topAnchor, constant: -20)
            ]
        )
        
        // 이미지 뷰 스케일 조정 및 스크롤뷰 적용
        imageView.image = self.image

        let scalingTargetSize = zoomView.frame.size
        let scaledSize = scaleSize(from: self.image!.size, to: scalingTargetSize)
        imageView.frame = CGRect(x: 0, y: 0, width: scaledSize.width, height: scaledSize.height)
        
        zoomView.contentSize = imageView.frame.size
        zoomView.addSubview(imageView)
    }
    
    @objc func okButton(_ sender: UIButton) {
        
        self.presentingViewController?.dismiss(animated: false, completion: { [weak self] in
            self?.complete?(true)
        })
    }
    
    @objc func cancelButton(_ sender: UIButton) {
        self.presentingViewController?.dismiss(animated: false, completion: { [weak self] in
            self?.complete?(false)
        })
    }
    
    // 이미지 스케일 사이즈
    func scaleSize(from imageSize: CGSize, to maxSize:CGSize) -> CGSize {
        let ratio: CGFloat
        if imageSize.width > imageSize.height {
            ratio = maxSize.width / imageSize.width
        } else {
            ratio = maxSize.height / imageSize.height
        }
        
        let scaledSize = CGSize(width: imageSize.width * ratio, height: imageSize.height * ratio)
        return scaledSize
    }
}

// MARK: UIScrollViewDelegate
extension ImageConfirmViewController: UIScrollViewDelegate {
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView.zoomScale <= 1.0 {
            scrollView.zoomScale = 1.0
        }
        
        if scrollView.zoomScale >= 3.0 {
            scrollView.zoomScale = 3.0
        }
    }
}

 

[사용방법]

let imageVC = ImageConfirmViewController()
imageVC.modalPresentationStyle = .fullScreen
imageVC.image = image // 적용할 이미지 
            
imageVC.complete = { [weak self] (isState) in
                
	if isState == true {
		// 확인
	} else {
		// 취소
	}
}
            
present(imageVC, animated: true)

+ Recent posts