6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Swift]PageViewControllerを使用したチュートリアルを実装する過程を記録してみた

Posted at

始めに

可愛いチュートリアル画面を探していたときに、Figmaのアプリを見つけた。
ログインする前に紹介ページを簡単に見れるのは良いなと思ったので作成してみる。

Storyboardでなく、コードでの実装となりますが、
Storyboardでやる場合も何となく雰囲気は掴めると思います!

作成したい画面

上部がスライドできるようになっている!
   

実装するには?

実装するには下記の知識が必要そうだ。

  • ContainerView
  • UIPageViewController
  • UIPageControl

ContainerViewとは?

ChatGPTに聞いてみた。

Swiftでは、ContainerViewは親ビュー内に他のビューコントローラのコンテンツを埋め込むための便利な方法です。ContainerViewは、親ビューコントローラ内に子ビューコントローラのビューを配置し、子ビューコントローラのライフサイクルを管理します。

Figmaの例で考えると、スライド箇所には別のViewControllerが埋め込まれているということ!
使い方については、下記を参考にする!

UIPageViewControllerとは?

ChatGPTに聞いてみた。

UIPageViewControllerは、スワイプジェスチャーやボタンなどのユーザーアクションに応じて、異なるビューコントローラを表示することができます。これにより、画面をスライドすることで複数のコンテンツページを操作することができます。典型的な使用例は、本のページめくりのようなアプリケーションや、画像ギャラリーなどです。

UIPageViewControllerは、ページ間のトランジションやアニメーションを管理し、表示するビューコントローラのスタックを管理します。さまざまな表示モードをサポートし、横スクロールや縦スクロール、ページめくりのようなアニメーションなどをカスタマイズすることもできます。

UIPageViewControllerは、ページ間のナビゲーションを簡単に実装するための便利なクラスであり、多くのアプリケーションで使用されています。

使い方については、下記を参考にするとわかりやすい!

UIPageControlとは?

ChatGPTに聞いてみた。

UIPageControlは、iOSアプリケーションでページングコンテンツを管理するためのユーザーインターフェース要素です。UIPageControlは、画面の下部などに表示され、現在のページ位置を示すドットまたはインジケーターを表示します。

通常、UIPageControlはUIScrollViewやUIPageViewControllerなどのビューコンテナと組み合わせて使用されます。ユーザーがスクロールすると、UIPageControlのドットも対応するように更新され、現在表示されているページの位置を示します。

UIPageControlは、通常は現在のページとそれ以外のページを区別するために、異なる色やスタイルのドットを使用します。ユーザーはドットをタップすることで、特定のページに直接移動することもできます。

UIPageControlは、ユーザーにコンテンツのスクロール可能性を示し、ページ間のナビゲーションを簡単にするための視覚的な手段として非常に便利です。

使い方については、下記を参考にするとわかりやすい!

UIPageControlは、UIPageViewControllerにてデフォルトで使用できるようになっているため、新規で作成しなくても大丈夫!

実際にコードを書いていく

使い方を知れたところで実際に使って慣らしていく!

実践: Figmaアプリを真似る

1. レイアウトの作成
どの端末でも潰れないようAutoLayoutを設定する。

完成図

大事なのは、containerViewUIViewで定義してあること。このUIViewにPageViewControllerを追加して表示する。

AuthenticationViewController.swift
import UIKit

class AuthenticationViewController: UIViewController {
    private let containerView = UIView()
    // 省略
}

2. PageViewControllerに表示するIntroductionViewConotrollerを作成する。
Figmaアプリの仕様は、全てのページでImageとTextが表示されているので、一つのViewControllerを共通して使うように設定する。

IntroductionViewController.swift
import UIKit

class IntroductionViewController: UIViewController {
    
    private let stackView = UIStackView()
    private let introductionImageView = UIImageView()
    private let introductionLabel = UILabel()
    
    
    // MARK: - View Life Cycle
    // ①
    init(introductionImage: UIImage, introductionText: String) {
        introductionImageView.image = introductionImage
        introductionLabel.text = introductionText
        super.init()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setUp()
    }
    // 省略

}


①について、初期化時にUIImageStringをもらうように設定している。これにより、IntroductionViewControllerを複数使い回すことができるようになる。

3. PageViewControllerを作成する。
基本的なPageViewControllerの設定について理解できていれば問題なくできる。

MainPageViewController.swift
import UIKit

class MainPageViewController: UIPageViewController {
    
    private var controllers = [UIViewController]()
    // ①
    private let pageData: [(imageName: String, text: String)] = [
        ("page1", "study hard"),
        ("page2", "worry a lot"),
        ("page3", "grow up")
    ]
    
    
    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()
        
        setUp()
    }
    
    
    // MARK: - Action
    
    private func setUp() {
        setUpPageViewController()
    }
    
    private func setUpPageViewController() {
        // ②
        pageData.forEach {
            let introductionVC = IntroductionViewController(introductionImage: UIImage(named: $0.imageName)!,
                                                            introductionText: $0.text)
            controllers.append(introductionVC)
        }
        setViewControllers([controllers[0]], direction: .forward, animated: true)
        self.dataSource = self
    }
    
}


// MARK: - UIPageViewControllerDataSource

extension MainPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        if let index = controllers.firstIndex(of: viewController),
           index > 0 {
            return controllers[index - 1]
        }
        return nil
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        if let index = controllers.firstIndex(of: viewController),
           index < controllers.count - 1 {
            return controllers[index + 1]
        }
        return nil
    }
}

PageViewControllerの設定については、"UIPageControllerとは?"で確認する。それ以外の箇所について説明を書く。①については、pageViewConotrollerに表示する情報をページ毎に配列で持っている。ここの配列を修正するだけでページの削除と追加が可能になるので楽!②については、配列を順番に回していき、必要な数分IntroductionViewControllerを生成している。

4. ContainerViewにPageViewControllerを表示する。

完成図

基本的なContainerViewの設定について理解できていれば問題なくできる。

AuthenticationViewController.swift
import UIKit

class AuthenticationViewController: UIViewController {

    private let containerView = UIView()

    // 省略

    private func setUpMainPageViewController() {
        // ①
        let mainPageVC = MainPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
        addChild(mainPageVC)
        mainPageVC.view.translatesAutoresizingMaskIntoConstraints = false
        containerView.addSubview(mainPageVC.view)
        // ②
        NSLayoutConstraint.activate([
            mainPageVC.view.topAnchor.constraint(equalTo: containerView.topAnchor),
            mainPageVC.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            mainPageVC.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            mainPageVC.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
        ])
        mainPageVC.didMove(toParent: self)
    }
}

①については、"ContainerViewとは?"を参考にすれば理解できる。②については、AutoLayoutの設定になるので、下記記事を参考にする。

5. UIPageControlを使用して、ページ数を可視化する。

完成図
   

MainPageViewController.swift
import UIKit

class MainPageViewController: UIPageViewController {
    
    private var controllers = [UIViewController]()

    
    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()
        
        setUp()
    }
    
    
    // MARK: - Action
    
    private func setUp() {
        setUpPageViewController()
    }
    
    private func setUpPageViewController() {
        // 省略
        // ①
        self.delegate = self
        // ②
        UIPageControl.appearance().pageIndicatorTintColor = .systemGray6
        UIPageControl.appearance().currentPageIndicatorTintColor = .black
    }
    
}

// MARK: - UIPageViewControllerDelegate
// ①
extension MainPageViewController: UIPageViewControllerDelegate {
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return controllers.count
    }

    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        return 0
    }
}

①について、Indicatorを表示するために必要な処理。presentationCuntメソッドで総数を返す。presentationIndexで最初に表示するViewControllerのIndexを返す。ここでの処理を忘れると表示されないので注意する。Indicatorを表示する処理については、"現在のページ位置の表示"に詳しく載っているので、読んでおくと良い!例えばボタンをタップした場合でもスクロールをさせたいとなると追加して処理を書く必要も出てくるので‥。
②について、マルポチの背景色と、選択された場合の色を指定している。UIPageControl.appearance()に対して設定をすると、全てのUIPageControlに対して設定が適用されるので注意する。色の変更処理に関しては、"ページインジケータの色を変える"に詳しく載っているので、読んでおくと良い!

終わりに

ここまでご覧いただきありがとうございました!
また、優れた知識と経験を共有してくださる皆様に感謝です!本当にありがとうございました!

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?