UIPageViewController
페이지 뷰 컨트롤러는 제스처를 사용하여 직접 제어할 수 있고,
페이지 간에 이동할 때 사용자가 지정한 전환을 사용하여 변경사항을 애니메이션화할 수 있다.
UIPageViewController를 만들기 위해서 스보에 총 4개의 뷰컨을 올려줬다.
첫 뷰컨은 UIPageViewController를 상속시켜주고, 나머지 3개는 UIViewController 클래스로 만들어준다.
1. 페이지뷰컨은 여러개의 뷰컨을 담고 있기 때문에 뷰컨트롤러 배열 타입의 프로퍼티를 하나 선언해준다.
var pageViewControllerList: [UIViewController] = []
2. 뷰컨 배열에 뷰컨을 담아준다.
private func createPageViewController() {
let storyboard = UIStoryboard(name: "Onboard", bundle: nil)
let vc1 = storyboard.instantiateViewController(withIdentifier: FirstViewController.reuseIdentifier) as! FirstViewController
let vc2 = storyboard.instantiateViewController(withIdentifier: SecondViewController.reuseIdentifier) as! SecondViewController
let vc3 = storyboard.instantiateViewController(withIdentifier: ThirdViewController.reuseIdentifier) as! ThirdViewController
pageViewControllerList = [vc1, vc2, vc3]
}
3. 페이지뷰컨의 첫 번째 뷰컨을 설정해준다.
private func configurePageViewController() {
delegate = self
dataSource = self
guard let first = pageViewControllerList.first else { return }
setViewControllers([first], direction: .forward, animated: true)
}
- UIPageViewController에는 2개의 프로토콜이 있어서 위임처리까지 해준다.
UIPageViewControllerDelegate, UIPageViewControllerDataSource
4. 프로토콜 메소드에서 현재 뷰컨 기준으로 이전에 보여줄 뷰컨을 리스트에서 가져오기
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
// 현재 페이지뷰컨에 보이는 뷰컨의 인덱스를 가져오기
guard let viewControllerIndex = pageViewControllerList.firstIndex(of: viewController) else { return nil }
// 왜 1을 빼주냐면, beforeIndex를 가져와야 하기 때문에 -> 0,1,2 있으면 2번 기준으로 1번을 보여주는 것
let previousIndex = viewControllerIndex - 1 // -1
// 0보다 작은 인덱스는 없으니까 nil이고, 그게 아니라면 리스트에서 가져오는 것
return previousIndex < 0 ? nil : pageViewControllerList[previousIndex]
}
5. 프로토콜 메소드에서 현재 뷰컨 기준으로 이후에 보여줄 뷰컨을 리스트에서 가져오기
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pageViewControllerList.firstIndex(of: viewController) else { return nil }
let nextIndex = viewControllerIndex + 1
// nextIndex가 리스트보다 값이 크면 가져올 수 없으니까 nil이다.
return nextIndex >= pageViewControllerList.count ? nil : pageViewControllerList[nextIndex]
}
한 화면에서 특정 영역에만 페이징을 주고 싶을 때,
예를 들어 마켓컬리처럼 윗 부분에만 페이징을 주고 싶을 때는 어떻게 해야 하나?
그럴 때는 Container View를 사용하면 된다.
이런 식으로 containerView를 뷰컨에 올리고 원하는 영역만큼 레이아웃을 잡아주고, 임베드해주는 과정을 거치면 기존 UIPageViewController가 containerView의 영역으로 사이즈가 줄어들게 된다.
그러면 만약에 현재의 entry point를 이렇게 UIPageViewController로 옮기면 어떻게 될까?
전체 뷰 사이즈로 잡히게 된다. 신기하잖아?
별개로, pageControl을 보여주고 싶은 경우
// pageControl 보여주기
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return pageViewControllerList.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
// display된 컨트롤러의 인덱스를 가져오는 것 / 근데 없을 경우에 0을 리턴
guard let first = viewControllers?.first,
let index = pageViewControllerList.firstIndex(of: first) else { return 0 }
return index
}
근데 UIPageViewController는 아주 기본적인 것으로 자주 쓰지는 않는다고 한다. 비슷하고 구현하고 싶다면, 컬렉션뷰로도 커스텀이 가능할 것 같다. 애플에서 제공하는 기본적인 UI로는 디쟈너가 원하는 대로 만들기 쉽지 않을 것 같다. 그래서 기본적으로 어떻게 코드가 동작하는지 이해하는 정도로 정리해두면 좋을 것 같다.
'⭐️ 개발 > iOS & Swift' 카테고리의 다른 글
[iOS] CodeBase로 가보자고 (0) | 2022.08.17 |
---|---|
[iOS/Swift] Custom Framework를 만들면서 배우는 접근제한자 (0) | 2022.08.16 |
[iOS] pngData(), jpegData(compressionQuality:) (0) | 2022.08.12 |
[iOS] cell에 delegate, datasource 코드를 작성하지 않는 이유... 어쩌구...저쩌구... (0) | 2022.08.12 |
[iOS] UIImagePickerController (0) | 2022.08.12 |