-
[UIKit] - 스크롤에 따라 이미지뷰를 줄이고 사라지게 만드는 방법 (UIKit + SnapKit)iOS/UIkit 2025. 5. 17. 13:52728x90반응형
많은 앱에서 상단 이미지(썸네일 또는 배너)가 스크롤에 따라 자연스럽게 축소되고 사라지는 UI를 볼 수 있다.
대표적으로는 Apple Music, App Store의 상세화면 등이 있다.
이번 글에서는 SnapKit + UIScrollViewDelegate를 활용해
이미지뷰의 높이와 투명도를 스크롤 위치에 따라 동적으로 조절하는 인터랙션을 구현해봤다.
🎯 구현 목표
- 스크롤하면 이미지뷰가 자연스럽게 축소된다.
- 충분히 스크롤하면 완전히 사라진다.
- 스크롤이 하단에 도달하면 원래 높이로 돌아가지 않도록 제어한다.
- SnapKit 사용 시 발생할 수 있는 제약 충돌을 방지한다.
🧱 View 구성 구조
DetailView ├── artworkImageView (썸네일) ├── scrollView │ └── contentStackView (스크롤 가능한 상세 콘텐츠)🧩 구현 포인트 요약
포인트설명
SnapKit .constraint 저장 안전하게 높이 업데이트 layoutSubviews()에서 초기 높이 저장 AutoLayout 반영 이후 시점 보장 offset / 200 → progress 비율 계산 부드러운 height 조절 alpha 조절 자연스러운 페이드 효과 스크롤 하단 도달 시 처리 차단 layout 복원 방지 1️⃣ 제약을 .constraint로 저장해 업데이트할 수 있게 만들기
SnapKit은 제약을 명시적으로 .constraint로 저장하지 않으면
update() 호출 시 “could not find existing matching constraint” 에러를 발생시킬 수 있다.
artworkImageView.snp.makeConstraints { $0.top.equalToSuperview() $0.width.equalTo(safeAreaLayoutGuide) artworkHeightConstraint = $0.height.equalTo(artworkImageView.snp.width).constraint }이렇게 하면 artworkHeightConstraint?.update(offset:)를 안전하게 호출할 수 있다.
2️⃣ 이미지뷰의 초기 높이는 layoutSubviews()에서 저장
뷰의 크기가 정확히 정해지는 시점은 layoutSubviews() 이후다.
따라서 이 시점에서 원본 height를 저장한다.
override func layoutSubviews() { super.layoutSubviews() self.originalArtworkHeight = artworkImageView.bounds.width }스크롤 비율 계산의 기준이 되는 값이므로 반드시 필요하다.
3️⃣ 스크롤 위치에 따라 높이와 투명도 조절하기
스크롤이 발생할 때마다 offsetY 값을 읽고, 이를 0~1 사이의 progress로 정규화한다.
이 값을 이용해 높이와 alpha를 함께 조절한다.
func updateArtworkImageHeight(by offsetY: CGFloat) { guard originalArtworkHeight > 0 else { return } let progress = min(max(offsetY / 200, 0), 1) // 0~1 범위 제한 let adjustedHeight = originalArtworkHeight * -progress artworkHeightConstraint?.update(offset: adjustedHeight) artworkImageView.alpha = 1 - progress }- offset이 0일 땐 progress = 0, 원래 크기 유지
- offset이 200 이상일 땐 progress = 1, 높이 0, alpha 0
4️⃣ 스크롤 하단에 도달했을 땐 이미지뷰를 조절하지 않도록 막기
스크롤이 완전히 아래로 내려가면, scrollView의 레이아웃 특성상
스크롤이 조금만 바뀌어도 다시 높이가 복원되는 문제가 생길 수 있다.
이를 막기 위해 아래 조건을 추가했다.
func scrollViewDidScroll(_ scrollView: UIScrollView) { let offset = scrollView.contentOffset.y let contentHeight = scrollView.contentSize.height let scrollViewHeight = scrollView.frame.size.height let isAtBottom = offset + scrollViewHeight >= contentHeight if !isAtBottom { detailView.updateArtworkImageHeight(by: offset) } }🧪 최종 코드 스니펫 요약
// 제약 저장 artworkHeightConstraint = $0.height.equalTo(artworkImageView.snp.width).constraint // 높이 + 투명도 동적 업데이트 func updateArtworkImageHeight(by offsetY: CGFloat) { let progress = min(max(offsetY / 200, 0), 1) artworkHeightConstraint?.update(offset: originalArtworkHeight * -progress) artworkImageView.alpha = 1 - progress } // 스크롤 조건 처리 func scrollViewDidScroll(_ scrollView: UIScrollView) { let offset = scrollView.contentOffset.y let isAtBottom = offset + scrollView.frame.height >= scrollView.contentSize.height if !isAtBottom { updateArtworkImageHeight(by: offset) } }✅ 마무리
이 기능은 단순해 보이지만 실제로 구현할 땐 여러 함정이 존재한다.
- SnapKit 제약 충돌
- 뷰 사이클 타이밍
- 스크롤 하단 처리
- 부자연스러운 alpha 변경
이러한 부분을 하나하나 정리하면서 구현했기 때문에,
UI가 부드럽고 안정적으로 작동하는 결과를 만들 수 있었다.
비슷한 인터랙션을 구현하고 싶다면 위 구조를 그대로 가져가도 좋다.
필요에 따라 blur 효과, 고정 타이틀 등으로 확장해보는 것도 추천한다.
'iOS > UIkit' 카테고리의 다른 글
[UIKit] - Tabbar 커스텀하기 (1) 2025.03.12 [UIKit] - AutoLayout (0) 2024.01.17 [UIKit] - XIB와 NIB (0) 2024.01.17 [UIkit] - 코드베이스 VS 스토리보드 (0) 2024.01.10