일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- struct
- 화면전환
- calendar
- uikit
- PushNotification
- IOS
- apns
- 글또
- SWIFTUI
- mvvm
- singleton
- error
- list
- Git
- escaping
- Refresh
- Animation
- ScrollView
- 고차함수
- Observer
- self
- SWIFT
- NotificationCenter
- protocol
- segue
- Switch
- class
- viewlifecycle
- http
- array
- Today
- Total
seong_hye, the developer
Swift) 책과 같이 페이지 넘기는 애니메이션에 대해 알아보기 (UIPageViewController) 본문
페이지를 넘길 때 종이가 말려서 넘어가는 듯한 느낌을 주고 싶을 때 사용하는 기능에 대해 알아보자
책을 넘기는 듯한 Page Curl은 SwiftUI에서는 기본 기능이 없어 UIPageViewController를 래핑해 사용함
📘UIPageViewController
UIKit의 컨테이너 뷰 컨트롤러
여러 페이지를 관리하고 스와이프 제스처나 애니메이션을 통해 페이지 간 전환을 제공
⚠️ Page Curl은 iPhone에서 제약이 존재(스택 구조 / 회전 등), 최신 iOS UI 가이드에선 스크롤형을 더 권장
🔹 생성 방법
let pageVC = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal,
options: nil
)
🔍 TransitionStyle
.scroll
가장 많이 사용됨, 수평/수직 스크롤 형태의 전환, 연속 페이징 가능
.pageCurl
종이 넘기듯 "책 넘김" 효과 (iPad에서 주로 사용됨, iPhone에선 제한적)
🔍 Navigation Orientation
.horizontal
좌우 넘김
.vertical
상하 넘김
🔹 주요 프로토콜
🔍 DataSource
페이지 전환 시 어떤 VC를 보여줄 지 지정하는 코드
func pageViewController(_ pvc: UIPageViewController, viewControllerBefore vc: UIViewController) -> UIViewController ?
func pageViewController(_ pvc: UIPageViewController, viewControllerAfter vc: UIViewController) -> UIIViewConttroller?
🔍 Delegate
페이지 전환 완료, 방향 등을 감지하는 코드
func pageViewController(_ pvc: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed Bool)
🔹 CODE
import SwiftUI
import UIKit
struct PageCurlPager<Page: View>: UIViewControllerRepresentable {
let pages: [Page]
func makeUIViewController(context: Context) -> UIPageViewController {
let vc = UIPageViewController(transitionStyle: .pageCurl, // 책 넘김 효과
navigationOrientation: .horizontal) // 좌우
// 페이지 컨트롤러 설정
vc.dataSource = context.coordinator
vc.delegate = context.coordinator
// 첫 페이지 설정
if let first = context.coordinator.controllers.first {
vc.setViewControllers([first], direction: .forward, animated: false)
}
// 양면처럼 보이게 하려면 true + spineLocation 조정
vc.isDoubleSided = false
return vc
}
func updateUIViewController(_ uiVC: UIPageViewController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self, pages.map { UIHostingController(rootView: $0.edgesIgnoringSafeArea(.all)) })
}
final class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
let parent: PageCurlPager
let controllers: [UIViewController]
init(_ parent: PageCurlPager, _ controllers: [UIViewController]) {
self.parent = parent
self.controllers = controllers
}
func pageViewController(_ pvc: UIPageViewController,
viewControllerBefore vc: UIViewController) -> UIViewController? {
guard let idx = controllers.firstIndex(of: vc), idx > 0 else { return nil }
return controllers[idx - 1]
}
func pageViewController(_ pvc: UIPageViewController,
viewControllerAfter vc: UIViewController) -> UIViewController? {
guard let idx = controllers.firstIndex(of: vc), idx < controllers.count - 1 else { return nil }
return controllers[idx + 1]
}
}
}
// 사용 예시
struct ContentView: View {
var body: some View {
PageCurlPager(pages: (0..<5).map { i in
ZStack {
Color(hue: Double(i)/5, saturation: 0.5, brightness: 0.9)
Text("Page \(i)").font(.largeTitle.bold())
}
})
}
}
📌 결과 화면
🔹 SwiftUI의 View를 넘기는 코드
UIPageViewController를 래핑해 사용할 때 UIViewController 배열을 넘기게 됨
하지만 SwiftUI View 배열을 넘기고 싶은 경우에는 UIHostingController(rootView:)로 감싸면 됨
class Coordinator: NSObject, UIPageViewControllerDataSource {
var parent: PageCurlPager
var controllers: [UIViewController]
init(_ parent: PageCurlPager) {
self.parent = parent
// SwiftUI View → UIHostingController 변환
self.controllers = parent.views.map { UIHostingController(rootView: $0) }
}
...
}
🔹 SwiftUI만 코드 짜는 것이 가능한가?
SwiftUI만으로는 PageCurl 효과를 자연스럽게 재현하는 것은 힘듦
이팩트 자체가 3D 메쉬 변형, 동적 그림자, 종이 말림까지 필요하기 때문
~> UIKit의 UIPageViewController를 래핑해 SwiftUI에서 쓰는게 제일 간단, 안정적
🔹 사용하는 경우
책 / 잡지 / 앨범 뷰어 -> .pageCurl
온보딩 화면 (여러 화면 슬라이드) -> .scroll + pageControl
분량 많은 컨텐츠를 페이지 단위로 나누어 표현해야 할 때 사용
🔍 Page Control ( 페이지 인디케이터)
여러 페이지 중 현재 페이지 위치를 점(●○○) 형태로 보여주는 UI
UIKit의 UIPageControl을 직접 사용하거나 SwiftUI의 TabView와 .page 스타일을 쓰면 자동으로 사용됨
UIPageControl을 따로 추가해 Delegate에서 currentPage를 업데이트
UIPageViewController는 UIPageControl을 자동 관리하지 않으므로 수동으로 붙여야 함
func makeUIView(context: Context) -> UIPageControl {
let control = UIPageControl()
control.currentPageIndicatorTintColor = .black
control.pageIndicatorTintColor = .lightGray
control.addTarget(context.coordinator, action: #selector(Coordinator.changed), for: .valueChanged)
return control
}
// 페이지 전환 완료 → PageControl 업데이트
func pageViewController(_ pvc: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool) {
if completed, let currentVC = pvc.viewControllers?.first,
let index = pages.firstIndex(of: currentVC) {
pageControl.currentPage = index
}
}
🔹 주의할 점
.pageCurl은 iPhone에선 UI제약이 있고, SplitView / iPad 환경에서 더 자연스러움
메모리 관리: 페이지가 많으면 UIHostingControoler를 매번 만들지 않고 캐싱을 고려해야 함
'IOS > UIKit' 카테고리의 다른 글
UIKit) Calendar / Weekly Calendar 구현해보기 (2) | 2024.02.04 |
---|---|
iOS) Calendar에 대해 알아보기 (0) | 2023.09.19 |
Swift) UISearchController에 대해 알아보기 (0) | 2022.11.08 |
Swift) UIActivityViewController에 대해 알아보기 (ShareSheet) (0) | 2022.11.06 |
UIKit) UICollectionvView - layout vs flowlayout (0) | 2022.11.06 |