
커스텀 이미지 피커 뷰 화면 애니메이션
주요 구현 View 3가지
1. 사용자 이미지 목록 View

- 이미지 썸네일 클릭시 클릭 순서에 맞게 데이터를 보관할 필요가 있다.
- 살짝 이미지를 어둡게 가리고 순서에 맞는 숫자를 보여줄 필요가 있다.
2. 선택한 이미지 목록 View

- 선택한 이미지를 순서에 맞게 보여줄 필요가 있다.
- 클릭하면 이미지를 삭제시키고 보관한 데이터를 변경해 줄 필요가 있다.
3. 사용자 앨범 목록 View


- 네이게이션 타이틀을 클릭하면 아래로 내려왔다가 다시 올라가는 화면 이동을 보여줘야한다.
이미지 클릭 시, 선택한 이미지 목록 View 올라오게 하기

- 선택한 이미지가 존재하면 선택한 이미지 목록 View 위로 올리는 애니메이션, 없으면 아래로 내리는 애니메이션
- 사용자 이미지 목록 View의 Constraint 변경
구현 예시
예시 코드 프로퍼티, 메서드 정의
- collectionView ⇒ 사용자 이미지 목록 View
- pinCreatingBottomView ⇒ 선택한 이미지 목록 View
- albumSelector ⇒ 사용자 앨범 목록 View
- func configureConstraints(){} ⇒ Constraint 설정하는 메서드, viewDidLoad에서 가장 먼저 실행
Constraint 관련 코드
final class CreatingPinVC:UIViewController{
var pinConstraint:Constraint?
var bottomConstraint: Constraint?
...
func configureConstraints() {
...
pinCreatingBottomView.snp.makeConstraints { make in
make.horizontalEdges.equalTo(view.safeAreaLayoutGuide)
make.height.equalTo(88)
make.bottom.equalTo(view.safeAreaLayoutGuide)
}
collectionView.snp.makeConstraints { make in
make.top.horizontalEdges.equalTo(view.safeAreaLayoutGuide)
self.pinConstraint = make.bottom.equalTo(pinCreatingBottomView.snp.top).constraint
self.bottomConstraint = make.bottom.equalTo(view.safeAreaLayoutGuide).constraint
}
...
pinConstraint?.deactivate()
bottomConstraint?.activate()
}
...
}
미리 사용자 이미지 목록 View의 Constraint를 두개를 만들고 저장변수에 담아둔다.
애니메이션 로직 코드
final class CreatingPinVC: UIViewController{
...
func imageSelectorDrawer(isEmpty: Bool){
let height = self.pinCreatingBottomView.bounds.height + 32
pinCreatingBottomView.transform = .init(translationX: 0, y: isEmpty ? height : 0)
}
...
}
- 이미지 존재하지 않으면 뷰를 아래로 내린다.
애니메이션 적용 코드
final class CreatingPinVC: UIViewController{
...
override func viewDidLoad() {
super.viewDidLoad()
configureConstraints()
...
vm.selectedAlbums.map{$0.isEmpty}.debounce(.milliseconds(200), scheduler: MainScheduler.instance).bind(with: self) { owner, isEmpty in
Task{@MainActor in
if isEmpty{
owner.pinConstraint?.deactivate()
owner.bottomConstraint?.activate()
owner.collectionView.layoutIfNeeded()
try await Task.sleep(for: .seconds(0.2))
}
UIView.animate(withDuration: 0.3) {
owner.imageSelectorDrawer(isEmpty: isEmpty)
}completion: { _ in
if !isEmpty{
owner.pinConstraint?.activate()
owner.bottomConstraint?.deactivate()
owner.collectionView.layoutIfNeeded()
}
}
}
}.disposed(by: disposeBag)
...
}
}
- 선택한 사진이 존재하는지 유무를 Bool 처리
- debounce 처리로 유저 액션 중첩 발생 처리
- 선택한 사진이 존재하는지에 따른 로직은 2가지가 존재
- 선택한 사진이 없는 경우:
- 사용자 이미지 목록 View에 BottomConstraint를 뷰 컨트롤러 하단으로 변경하는 작업 후 애니메이션 작동
- 선택한 사진이 존재하는 경우:
- 애니메이션 작동 후 사용자 이미지 목록 View에 BottomConstraint를 선택한 이미지 목록 View 상단으로 변경하는 작업
⚠️ 애니메이션 작동에 따라 Constraint 변경 작업 시기를 다르게 배치해서 메인 쓰레드 충돌을 없앤다.
+ 이미지가 비어있을 때, 선택한 이미지 목록 View DiffableDataSource 처리
final class BottomDataSource:UICollectionViewDiffableDataSource<String,AlbumItem.ID>{
...
@MainActor func resetSnapshot(albumIDs:[AlbumItem.ID]){
var snapshot = NSDiffableDataSourceSnapshot<String,AlbumItem.ID>()
snapshot.appendSections(["hello"])
snapshot.appendItems(albumIDs)
Task{@MainActor in
if albumIDs.isEmpty{
try await Task.sleep(for: .seconds(0.66))
}
await apply(snapshot,animatingDifferences: true)
}
}
...
}
💡 위에서 처리한 선택한 이미지 목록 View가 아래로 내려가는 Animation 작동 후, 셀 아이템이 사라져야 한다.
그렇기 때문에 일정 시간이 지나고 비어있는 Snapshot을 apply한다.
사용자 앨범 목록 View
애니메이션 로직 코드
final class CreatingPinVC: UIViewController{
...
func albumSelectorDrawer(isOpen: Bool){
let bounds = albumSelector.bounds
let height = bounds.height
let ny = bounds.origin.y
albumSelector.bounds = .init(origin: bounds.origin, size: .init(width: bounds.width, height: height - ny))
albumSelector.transform = .init(translationX: 0, y: isOpen ? 0: -(height - ny))
}
}
애니메이션 적용 코드
final class CreatingPinVC: UIViewController{
...
override func viewDidLoad() {
super.viewDidLoad()
...
vm.openAlbumSelector.debounce(.milliseconds(200), scheduler: MainScheduler.instance).bind(with: self) { owner, val in
UIView.animate(withDuration: 0.33) {
owner.albumSelectorDrawer(isOpen: val)
}
}.disposed(by: disposeBag)
...
}
...
}
초기 애니메이션 진행 막기

발생하는 이유..?
초기값을 isOpen = false, isEmpty = true로 설정해도 present 직후 animation 작동이 완료되지 않음…
👉 viewDidAppear에서 일정 시간이 지나서 선택한 이미지 목록 View, 사용자 앨범 목록 View의 Hidden 처리를 false로 바꾼다.
final class CreatingPinVC: UIViewController{
...
override func viewDidLoad() {
...
pinCreatingBottomView.isHidden = true
albumSelector.isHidden = true
...
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Task{@MainActor in
try await Task.sleep(for: .seconds(0.2))
pinCreatingBottomView.isHidden = false
albumSelector.isHidden = false
}
}
}'이모저모 > UIKit' 카테고리의 다른 글
| UIHostingConfiguration을 이용한 컬렉션 뷰 채팅 셀 (0) | 2024.09.03 |
|---|---|
| UIImage 메모리 최적화 (0) | 2023.12.11 |
| 커스텀 이미지 피커 뷰 만들기 #1 (0) | 2023.12.02 |
| 싱글톤 앨범 이미지 서비스 만들기 with Rx or Combine (0) | 2023.11.28 |
| UIButton State 애니메이션용 모듈 만들고 적용하기 (0) | 2023.11.25 |
댓글