(Swift) UIScrollView

UIScrollView

스크롤뷰는 뷰의 크기보다도 더 큰 콘텐츠를 표시해야할 때 사용되는 UIView의 서브 클래스로 흔히 포토 뷰어 등에 쓰이는 컨트롤이다.

스크롤뷰는 자체적으로 사용자의 터치 제스쳐를 인식하여 콘텐츠를 스크롤해준다. 스크롤 뷰는 내부적으로 표시를 위한 뷰 컴포넌트를 따로 갖고 있지는 않으며, 대신에 contentSize: CGSize라는 프로퍼티를 이용해서 스크롤뷰 자체가 화면에서 표시되는 크기에 대비하여 전체 콘텐츠 사이즈로 사용되는 영역이 얼마인지를 정의하고 이를 통해 어디서 어디까지 스크롤 할 것인지를 결정하게 된다.

기본 사용법

기본적인 스크롤뷰의 사용방법은 다음과 같다.

  1. 스크롤 뷰를 생성하고 frame 속성을 정의한 후, 이를 뷰에 추가한다.
  2. 스크롤 뷰 내에 표시할 콘텐츠로 사용될 뷰를 생성하고 이를 스크롤 뷰에 추가한다.
  3. 콘텐츠로 사용될 뷰의 크기를 스크롤뷰의 contentSize 프로퍼티로 정해준다.
  4. profit!
/// in a UIViewController
// ...
var scrollView: UIScrollView!
var imageView: UIImageView!

override func viewDidLoad() {
  super.viewDidLoad()
  // ...
  let frameSize = view.bounds.size
  scrollView = UIScrollView(frame: CGRect(origin: CGPoint.zero, size: frameSize)
  /// 이미지는 번들에 포함되어 있음을 가정한다.
  let iamge = UIImage(named: "sample")
  let iamgeView = UIImageView(image: image)
  scrollView.contentSize = imageView.bounds.size
  scrollView.addSubview(imageView)
  view.addSubView(scrollView)
  ...
 }

핀치를 통한 줌인/줌아웃

스크롤뷰를 사용하는 경우는 주로 이미지를 위한 것인데, 이미지를 스크롤뷰에 적용할 때에는 핀치 동작을 통해서 줌인/줌아웃을 적용해주는 경우가 많다. 이 부분은 공짜로(?) 적용되지는 않고, 스크롤 뷰 내의 어떤 뷰가 확대/축소될 것인지를 델리게이트를 통해서 지정해주어야 한다. 또한, 확대/축소의 비율이 최대 어느 범위까지 허용될지도 결정해주어야 한다. 기본적으로 이 값은 1.0으로 세팅되어 있기 때문에 이 값을 변경해주지 않으면 확대/축소가 일어나지 않는다.

override func viewDidLoad() {
  super.viewDidLoad()
  // ...
  let frameSize = view.bounds.size
  scrollView = UIScrollView(frame: CGRect(origin: CGPoint.zero, size: frameSize)
  /// 이미지는 번들에 포함되어 있음을 가정한다.
  let iamge = UIImage(named: "sample")
  iamgeView = UIImageView(image: image)

  scrollView.maximumZoomScale = 4.0
  scrollView.minimumZoonScale = 0.1

  scrollView.contentSize = imageView.bounds.size
  scrollView.addSubview(imageView)

  scrollView.delegate = self

  view.addSubView(scrollView)
  ...
 }

스크롤뷰의 델리게이트는 UIScrollViewDelegate 프로토콜을 따르는데 스크롤뷰에서 스크롤이 발생하거나 끝날 때, 줌이 시작되거나 끝날 때의 이벤트를 받아서 처리할 수 있는 메소드들이 정의되어 있다. 그리고 앞서 언급된 줌인/줌아웃에서 어떤 뷰가 확대축소될 것인지를 알려주는 메소드가 있다. 이 메소드를 정의해야 한다.

class ViewController: UIViewController, UIScrollViewDelegate {
// ...

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

더블 탭을 통한 줌

많은 포토 뷰어 들이 더블탭을 통해서 줌인/줌아웃을 하는 경우를 볼 수 있는데, 이 기능은 스크롤뷰에서 자체적으로 제공하는 기능이 아니다. 이는 UITapGestureRecognizer를 통해서 더블 탭을 인식하고, 스크롤 뷰의 setZoomScale(_:animated:)zoom(to:animated:)를 호출하여 줌되게 할 수 있다.

override viewDidLoad() {
  ...
  let doubleTap = UITapGestureRecognizer(target: self, action: #selector(self.tapToZoom))
  doubleTap.numberOfTapsRequired = 2
  doubleTap.numberOfTouchesRequired = 2
  scrollView.addGestureRecognizer(doubleTap)
  ...
}

/*
    1보다 작을 때는 1로 키운다.
    1보다 클 때는 최대로 키운다
    최대일 때는 최소로 만든다.
*/
func tapToZoom(_ gestureRecognizer: UIGestureRecognizer) {
  switch scrollView.zoomScale {
  case (scrollView.minimumZoomScale..<1.0):
    scrollView.setZoomScale(1.0, animated: true)
  case (1.0..<scrollView.maximumZoomScale):
    scrollView.setZoomScale(1.0, animated: true)
  case scrollView.maximumZoomScale:
    scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
  default:
    scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true)
  }
}