Danny의 iOS 컨닝페이퍼
article thumbnail

[TIL #10] 2023 / 04 / 09

프로젝트에서 스크롤뷰를 사용 중인데, 문제가 발생했습니다.

 

현재 상황은 스크롤 뷰 안쪽에 스택뷰를 배치하고,

스택뷰의 레이아웃을 스크롤 뷰의 중심으로 맞춰서 모양을 만들어 줬습니다.

self.addSubview(photoScrollView)
photoScrollView.snp.makeConstraints {
    $0.top.equalTo(photoLabel.snp.bottom).offset(titleGap-space)
    $0.trailing.leading.equalToSuperview().inset(sideInset)
    $0.height.equalTo(imageSize+space)
}

photoScrollView.addSubview(photoStackView)
photoStackView.snp.makeConstraints {
    $0.centerX.equalToSuperview()
    // ⭐️ 스크롤 뷰를 사용할 때는 스크롤뷰 끝 부분(콘텐츠뷰 끝점)의 레이아웃을 꼭 잡아줘야 한다!!!
    $0.trailing.equalToSuperview()
}

회색 영역이 스크롤 뷰, 버튼을 스택뷰로 만들어서 이미지를 추가할 때마다 스택뷰에 쌓는 형식으로 만들었습니다.

 

저번에 배운 [Swift/TIL #9] PHPickerViewController에 대하여 를 통하여,

스택뷰 안쪽에 이미지를 추가할 수 있게 만들어 줬는데,

여러 개의 이미지를 추가하면, 콘텐츠뷰 영역이 스택뷰 영역을 벗어나서 첫 번째 사진이 짤리는 현상이 발생했습니다...

(스택뷰가 스크롤 가능한 영역보다 커짐)

 

photoStackView의 왼쪽(leading)도 레이아웃을 잡으면 이런 문제는 발생하지 않지만,

그러면 또 스택뷰를 중간에 위치시킬 수 없어요.

 

저는 스택뷰를 항상 중간에 위치시켜서, 사진을 추가해도 비율이 맞게 중간정렬이 됐으면 했습니다.

 

여러 방법을 시도해 보다가

그나마 생각대로 동작하는 로직을 만들어봤습니다.

(시도한 내용은 마무리를 참고하세요.)

 

 

Contents View를 조절하는 로직을 추가해 보자

여기 사용된 해결방법은 contentInset을 이용해 조정해주는 겁니다.

 

그러면 스택뷰의 사진이 추가될 때마다, Contents View의 왼편 가장자리 공간을

contentInset을 통해 크기를 키워주는 방식으로 만들었습니다.

private func photoContentViewInsetUpdate() {
    
    // 이미지가 추가될 때 마다 커지는 크기
    let imageSizeWithSpace = ((diaryView.imageSize) / 2) + diaryView.space
    var inset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    
    // 현재 사진 갯수에 따라 ContentsView의 왼쪽 크기를 키움
    for num in 0 ..< selections.count {
        inset.left = imageSizeWithSpace * CGFloat(num - 1)
    }

    // 이미지가 3개 이상일 때 contentInset 변경, 아니라면 초기화
    if selections.count >= 3 {
        diaryView.photoScrollView.contentInset = inset
    } else {
        diaryView.photoScrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }
}

 

이렇게 만들어 주면, Contents View의 왼쪽 부분도

사진의 갯수가 증가함에 따라 크기를 커지게 만들 수 있습니다.

 

추가로, scrollViewDidChangeAdjustedContentInset 델리게이트를 통해

처음 스크롤뷰에 보이는 위치도 설정해 줬습니다.

(Contents View의 크기가 변할 때마다 호출 되는 메서드)

 

사진을 추가해도 항상 마지막에 버튼이 보이도록 만들어 봤습니다.

extension DiaryViewController: UIScrollViewDelegate {
    // 스택뷰에 이미지를 추가함에 따라 스크롤뷰의 콘텐츠뷰의 크기를 증가시키는 메서드
    private func photoContentViewInsetUpdate() {
        diaryView.photoScrollView.delegate = self
        
        // 이미지가 추가될 때 마다 커지는 크기
        let imageSizeWithSpace = ((diaryView.imageSize) / 2) + diaryView.space
        var inset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        
        // 현재 사진 갯수에 따라 콘텐츠뷰의 크기를 키움
        for num in 0 ..< selections.count {
            inset.left = imageSizeWithSpace * CGFloat(num - 1)
        }
    
        // 이미지가 3개 이상일 때 contentInset 변경, 아니라면 초기화
        if selections.count >= 3 {
            diaryView.photoScrollView.contentInset = inset
        } else {
            diaryView.photoScrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        }
    }
    
    // 👉 콘텐츠뷰의 크기가 변할 때 마다 스크롤뷰의 위치를 이동 시켜줌, 스크롤뷰에서 보이는 화면을 마지막 버튼으로 이동
    func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) {
        let x = scrollView.adjustedContentInset.left
        scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: true)
    }

}

 

다만 문제점이... 기종마다 화면 크기가 달라서 그런가

아이폰 SE 같은 경우 미세하게 중간 정렬이 어긋나네요...

(정말 미세하게...)

 

차후 다시 생각해봐야 할 것 같습니다.

더 좋은 방법을 아시면 댓글 부탁드립니다!!!

 

 

마무리

처음엔 레이아웃 잡는 부분에서 설정을 시도했는데,

만들어준 뷰가 아직 생성이 되기 전이라 그런지 동적으로 변하는 값을 얻을 수 없더라고요.

 

다음으로 생각한 방법이

viewDidLayoutSubviews을 통해 직접 스택뷰의 레이아웃 위치도 변경을 해봤지만,

이 방법도 역시 별로 좋은 방법은 아닌 것 같더라고요.

 

그래서 일단 UIScrollView 공식문서 에서 쓸만한 API를 찾아보던 중

contentInset이 있길래 곰곰이 생각을 해봤습니다.

 

"UIScrollView는 안쪽에는 보여지는 영역인 Contents View가 있고,

현재 보여지는 영역(contentView)보다 스택뷰가 커서 이미지가 짤리는 거니까!

그럼 Contents View를 키워주면 되지 않을까?" 라는 생각에 이와 같이 만들어봤는데...

 

일단 생각대로 동작은 되는 것 같네요 ㅎ

 

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!