[Swift] CollectionView로 ImagePager만들기 (2)

민경준·2021년 11월 9일
2

저번에 imagePager의 기초단계를 만들어보는 시간을 가졌었는데요
이번에는 image를 확대하는 방법에 대해서 알아보도록 합시다.

2. 이미지 확대 기능 추가하기

2-1. Cell 안에 있는 ScrollView의 Delegate를 설정한다.

override init(frame: CGRect) {
    super.init(frame: frame)
    .
    .
    .
    
    /// 스크롤뷰의 zoom관련 delegate 함수를 사용하기 위해
    /// scrollView의 delegate를 설정한다.
    self.scrollView.delegate = self
}

2-2. scrollView의 zoom관련 delegate 함수를 설정한다.

extension ImagePageCell: UIScrollViewDelegate {
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        /// 스크롤 뷰에서 확대/축소가 발생하려고 할 때
        /// 지정된 view를 확대/축소 하도록 하는 함수
    }
    
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        /// 스크롤 뷰에서 확대/축소가 발생했을 때 호출되는 함수
    }
}

자, 그럼 우리는 스크롤뷰를 확대/축소 했을 때 어떤걸 조절해야 할까요?
imagePager를 만들고 있으니 당연히 이미지뷰를 확대/축소 하도록 지정해줘야 합니다.

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    /// 스크롤 뷰에서 확대/축소가 발생하려고 할 때
    /// 지정된 view를 확대/축소 하도록 하는 함수
        
    return self.imageView
}

그리고 확대/축소가 발생했을 때는 어떤 작업을들 해줘야 할까요?

먼저, 확대/축소가 발생했을 때 collectionView의 스크롤이 움직이면 안되겠죠?
그걸 방지하기 위해 현재 scrollView의 zoomScale 값을 통해 스크롤 가능 여부를 정해주도록 합니다.

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    /// 스크롤 뷰에서 확대/축소가 발생했을 때 호출되는 함수
    
    guard let colletionView = self.superview as? UICollectionView else { return }
        
    /// enlarged -> 현재 ZoomScale이 늘어나 있는 상태인지 여부
    let enlarged = scrollView.zoomScale > 1
    /// 현재 ZoomScale이 늘어나있다면 스크롤이 불가능하도록 처리한다.
    colletionView.isScrollEnabled = !enlarged
}

ZoomScale이 늘어나있다면 해야할 작업이 더 있습니다.
바로 스크롤뷰의 contentInset을 새로 적용시켜줘야 합니다.

왜냐구요? 그렇지 않으면 Zoom을 한 뒤에 사진을 위로 올리거나 아래로 내릴 때
공간이 남기때문에 사진의 끝에 걸리지 않고 scrollView의 끝까지 올라가기 때문이죠.
백문이 불여일견이라고 contentInset을 적용안한 상태의 화면을 보도록 합시다.

보시다시피 화면이 확대되어 있을때는 collectionView가 스크롤이 되지 않고 scrollView만 스크롤되고 있죠?

근데 사진을 끝까지 내리거나 올리게되면 공간이 남게되어서 사진이 가려버리는 불상사가 생기게 되버리네요 ㅠㅠ

그래서 ZoomScale이 커져있는 상태에서는 contentInset에 마이너스 값을 주어서 사진끝에 걸리도록 만들어 줘야합니다.

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    /// 스크롤 뷰에서 확대/축소가 발생했을 때 호출되는 함수
    
    guard let colletionView = self.superview as? UICollectionView else { return }
        
    /// enlarged -> 현재 ZoomScale이 늘어나 있는 상태인지 여부
    let enlarged = scrollView.zoomScale > 1
    /// 현재 ZoomScale이 늘어나있다면 스크롤이 불가능하도록 처리한다.
    colletionView.isScrollEnabled = !enlarged
    
    guard let image = self.image else { return }
    
    if enlarged {
        let ratioW = self.imageView.frame.width / image.size.width
        let ratioH = self.imageView.frame.height / image.size.height
        
        /// 사진의 크기가 어느 비율에 맞춰져 있는지 체크한다.
        /// 예를들어 200X200 사이즈의 이미지가 있고
        /// 414X200 사이즈의 imageView가 있다고 가정할 때
        /// 세로 비율 = 2.07 , 가로 비율 = 1.0 이다.
        /// imageView의 contentMode가 scaleAspectFit이기 때문에
        /// 더 작은 비율에 맞춰서 사이즈가 조절되어 있을것이다.
        /// 그러므로 더 작은 비율을 가져다 사용해야 한다.
        let ratio = ratioW < ratioH ? ratioW : ratioH
        
        /// 비율에 맞는 이미지 사이즈를 계산한다.
        let imageWidth = image.size.width * ratio
        let imageHeight = image.size.height * ratio
        
        /// (이미지의 가로길이 * 현재 줌 스케일) 크기가
        /// 이미지뷰의 가로 길이보다 큰지 여부를 가리는 조건식
        let leftCondition = imageWidth * scrollView.zoomScale > self.imageView.frame.width
        var leftInset = leftCondition
            /// 크다면 inset이 필요없기 때문에 0이 들어가도록 계산한다.
            ? imageWidth - self.imageView.frame.width
            /// 작다면 inset에 마이너스 값을 줘야 하기 때문에
            /// ScrollView 가로길이에서 ContentSize의 가로길이를 뺀다.
            : scrollView.frame.width - scrollView.contentSize.width
        /// 양 옆에 나눠서 적용해야 하기때문에 절반 사이즈로 계산한다.
        leftInset = leftInset * 0.5
        
        /// (이미지의 세로길이 * 현재 줌 스케일) 크기가
        /// 이미지뷰의 세로 길이보다 큰지 여부를 가리는 조건식
        let topCondition = imageHeight * scrollView.zoomScale > self.imageView.frame.height
        var topInset = topCondition
            /// 크다면 inset이 필요없기 때문에 0이 들어가도록 계산한다.
            ? imageHeight - self.imageView.frame.height
            /// 작다면 inset에 마이너스 값을 줘야 하기 때문에
            /// ScrollView 세로길이에서 ContentSize의 세로길이를 뺀다.
            : scrollView.frame.height - scrollView.contentSize.height
        /// 위 아래에 나눠서 적용해야 하기때문에 절반 사이즈로 계산한다.
        topInset = topInset * 0.5
        
        /// 각각의 인셋들을 적용시켜준다.
        scrollView.contentInset = UIEdgeInsets(top: topInset, left: leftInset, bottom: topInset, right: leftInset)
    } else {
        /// ZoomScale이 원상태로 돌아온 경우에는 ContentInset을 없애준다.
        scrollView.contentInset = .zero
    }
}

자, 이제 Zoom에 관련한 설정을 모두 끝마쳤으니 결과물을 확인해 보도록 합시다.

Result

이 처럼 사진을 확대해서 스크롤을 하더라도 사진 끝에 걸려서 더이상 안내려가고 안올라가는게 보이시죠?? ㅎㅎㅎ

여기까지 하셨다면 거의 다 완성했다고 생각하실수도 있겟지만 한가지가 더 남았습니다.
바로 스크롤 다운으로 해당 화면을 내려버리는 심화과정이 남아있습니다 ㅎㅎ

이것은 이 다음 글에서 다루도록 하겠습니다.

profile
iOS Developer 💻

0개의 댓글