UIPageViewController
를 상속받는 클래스로 화면을 구성했다UIViewController
를 상속받는 6개의 클래스로 생성했다class OnboardingViewController: UIPageViewController {
override init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) {
// 페이지뷰의 스타일
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// 페이지로 활용할 배열 - 초기는 빈 배열
var list: [UIViewController] = [];
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGray2
list = [ViewController1(), ViewController2(), ViewController3(), ViewController4(), ViewController5(), ViewController6()]
// 프로토콜 연결
connectProtocol()
// 첫 화면 설정
guard let first = list.first else { return }
setViewControllers([first], direction: .forward, animated: true)
}
}
extension OnboardingViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
func connectProtocol() {
delegate = self
dataSource = self
}
// 비포
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let currentIndex = list.firstIndex(of: viewController) else { return nil }
return currentIndex <= 0 ? nil : list[currentIndex - 1]
}
// 애프터
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let currentIndex = list.firstIndex(of: viewController) else { return nil }
return currentIndex >= list.count - 1 ? nil : list[currentIndex + 1]
}
// 아래 똥글뱅이
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return list.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let first = viewControllers?.first, let index = list.firstIndex(of: first) else { return 0}
return index
}
}
"화면 번호"
"함수 이름"
으로 찍힌다맨 처음 화면
1 viewDidLoad
1 viewWillAppear
1 viewDidAppear
오른쪽으로 살짝 스크롤 (당기는 중)
2 viewDidLoad
2 viewWillAppear
1 viewWillDisappear
오른쪽 도착
2 viewDidAppear
1 viewDidDisappear
3 viewDidLoad
viewDidLoad
가 실행된다viewDidLoad
가 실행되는 경우도 있었따addTarget
을 연결해주는 부분에서 실수가 있었다// 기존 코드
button.addTarget(ViewController6.self, action: #selector(loginSuccess), for: .touchUpInside)
// 제대로 된 코드
button.addTarget(self, action: #selector(loginSuccess), for: .touchUpInside)
self
는 ViewController6 클래스의 생성된 인스턴스를 의미하고,ViewController6.self
는 ViewController6 자체의 타입을 의미한다고class SkipPagePracticeViewController: UIViewController {
// 하나의 객체로 선언한다
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
let skipButton = {
let button = UIButton()
button.setTitle("skip", for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 20)
button.backgroundColor = .white
button.setTitleColor(UIColor.black, for: .normal)
return button
}()
// 1에서는 빈 배열 생성 후, viewDidLoad에서 요소를 넣어주었는데,
// 여기서는 클로저 이용해서 바로 값을 넣어주었다
// 근데 왜 이렇게... 했지..? 그냥 초기화 할 때 넣어주면 안되냐..??
let viewList = {
let list = [ViewController1(), ViewController2(), ViewController3(), ViewController4(), ViewController5(), ViewController6()]
return list
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
pageViewController.dataSource = self
pageViewController.delegate = self
guard let first = viewList.first else { return }
pageViewController.setViewControllers([first], direction: .forward, animated: true)
view.addSubview(pageViewController.view)
pageViewController.view.snp.makeConstraints { make in
make.edges.equalTo(view)
}
view.addSubview(skipButton)
skipButton.snp.makeConstraints { make in
make.centerX.equalTo(view)
make.width.equalTo(200)
make.bottom.equalTo(view).inset(80)
}
skipButton.addTarget(self, action: #selector(skipButtonClicked), for: .touchUpInside)
}
// 바로 6번 화면으로 보내버린다
// 맨 처음 화면 로드하는 코드를 참고했다
@objc
func skipButtonClicked() {
guard let last = viewList.last else { return }
pageViewController.setViewControllers([last], direction: .forward, animated: true)
}
}
// extension 부분은 1과 거의 동일하다
addSubView
안에 넣어야 하나UIPageViewController
는 UIViewController
를 상속받고,UIViewController
클래스에는 맨날 쓰는 open var view: UIView!
가 있다.view
붙여주면 된다view.addSubview(pageViewController.view)
pageViewController.view.snp.makeConstraints { make in
~~~
}
그러다보니 상대적으로 아래에 있는 페이지뷰의 뷰가 넘어갈 때,
위에 있는 skip 버튼은 가운데에 그대로 위치해있다
보통 화면이 넘어갈 때는 모든 요소가 다 넘어가야 할 것 같아서
그 점이 좀 불편했다
View1Controller
에서 선언하고SkipPagePracticeVewController
에서 동작하는데// 프로토콜 선언
protocol SkipToEndDelegate: class {
func skipToEnd()
}
// 필요한 부분만 적고, 대부분 생략했다
// skip 버튼 생성 및 프로토콜 변수 생성
class ViewController1: UIViewController {
weak var delegate: SkipToEndDelegate?
let skipButton = {
let button = UIButton()
~~
return button
}
override func viewDidLoad() {
view.addSubview(skipButton)
skipButton.snp.makeConstraints { make in
~~
}
skipButton.addTarget(self, action: #selector(skipButtonClicked), for: .touchUpInside)
}
// 버튼을 누르면 -> 프로토콜 변수의 함수가 실행되게 한다
@objc
func skipButtonClicked() {
delegate?.skipToEnd()
}
}
// 각 뷰 클래스의 delegate를 현재 인스턴스로 잡아준다
class SkipPagePracticeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 업캐스팅 후 연결해준다
(viewList[0] as! ViewController1).delegate = self
(viewList[1] as! ViewController2).delegate = self
(viewList[2] as! ViewController3).delegate = self
(viewList[3] as! ViewController4).delegate = self
(viewList[4] as! ViewController5).delegate = self
}
}
// 프로토콜 채택 후 필수 메서드 정의
// 6번 화면으로 바로 넘어가는 코드 그대로 적어주었다 (기존 skipButton)
extension SkipPagePracticeViewController: SkipToEndDelegate {
func skipToEnd() {
guard let last = viewList.last else { return }
pageViewController.setViewControllers([last], direction: .forward, animated: true)
}
}
viewList
안에 여러 ViewController를 담았더니viewList
의 타입이 [UIViewController]
가 되었고,viewList[0]
의 타입은UIViewController
가 되었다delegate
를 선언해 둔 부분은UIViewController
를 상속받은 ViewController1
클래스이기 때문에UIViewController
클래스에서는 delegate
를 찾을 수 없다delegate
를 연결해줄 수 없었다UIViewController
)을 업캐스팅해서UIViewController1
)으로 바꾸어 주었고,