UIScrollViewとUIPageControlで別々のページを表示（iOS 14におけるUIPageControlの新機能の紹介を入れて）

ここでは、ユーザーがスクロールできるさまざまなページを表示するビューを設定する方法について説明します（`UIScrollView` と `UIPageControl` を使用）。また、iOS 14の新機能を使って、`UIPageControl` をカスタマイズする方法についても説明します。

<img width="500" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/ae652c4d-84e1-fc54-7129-2b2175b8ce44.png">

ここでは、以下について説明します：

異なるページを表示する：

- `UIScrollView` と `UIPageControl` を使用して、さまざまなページを表示する `UIKit` ビューをデザインする

iOS 14のみ：

- `UIPageControl` のカスタムインジケーター画像を設定する
- `UIPageControl` のバックグラウンドが常に見えるように設定する
- ユーザーが `UIPageControl` をプレスして、スクロールで別のページに切り替えられるようにする。

## 異なるページを表示するUIKitビューを設計します

[すでに終了したプロジェクトをGithubのここにダウンロードすることで、このセクションをスキップすることができます。](https://github.com/mszmagic/PageViewExample)

### 変数を準備します

```swift
private var views = [UIView]()
private var scrollView: UIScrollView!
private var pageControl: UIPageControl!
```

views変数には、UIScrollViewに追加したいすべてのUIViewが含まれています

### UIScrollViewを（あなたのviewDidLoadの）画面に追加します

```swift
scrollView = UIScrollView(frame: self.view.frame)
scrollView.isPagingEnabled = true
scrollView.backgroundColor = .clear
scrollView.delegate = self
self.view.addSubview(scrollView)
```

### 各ビューを `UIScrollView` に追加します

```swift
for i in 0..<views.count {
    let view = views[i]
    view.frame = CGRect(x: CGFloat(i) * self.view.frame.size.width, y: 200, width: self.view.frame.size.width, height: 300)
    scrollView.addSubview(view)
}
```

### `UIPageControl` を画面に追加します

```swift
pageControl = UIPageControl(frame: CGRect(origin: .zero, size: CGSize(width: view.bounds.width, height: 50)))
pageControl.numberOfPages = views.count
pageControl.currentPage = 0
pageControl.addTarget(self, action: #selector(self.pageUpdated), for: .valueChanged)
pageControl.pageIndicatorTintColor = .gray
pageControl.currentPageIndicatorTintColor = .white
view.addSubview(pageControl)
view.bringSubviewToFront(pageControl)
```

### プログラムのレイアウトを設定します

```swift
override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    // ScrollView
    scrollView.frame = self.view.frame
    let totalWidth = CGFloat(views.count) * self.view.frame.size.width
    scrollView.contentSize = CGSize(width: totalWidth, height: self.view.bounds.height)
    // UIPageControl
    pageControl.translatesAutoresizingMaskIntoConstraints = false
    let leading = pageControl.leadingAnchor.constraint(equalTo: view.leadingAnchor)
    let trailing = pageControl.trailingAnchor.constraint(equalTo: view.trailingAnchor)
    let bottom = pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50)
    view.addConstraints([leading, trailing, bottom])
}
```

### データ例

一部のビューの例を追加することができます：

```swift
func loadDemoData() {
    for i in 0..<3 {
        // UIKit
        let view = UITextView()
        view.text = "Test \(String(i))"
        view.textAlignment = .center
        view.font = .preferredFont(forTextStyle: .largeTitle)
        view.isSelectable = false
        view.isEditable = false
        views.append(view)
    }
}
```

または、一部のSwiftUIビューを追加することができます

```swift
func loadSwiftUIViews<Content: View>(_ swiftUIViews: [Content]) {
    for view in swiftUIViews {
        let hostingVC = UIHostingController(rootView: view)
        addChild(hostingVC)
        hostingVC.didMove(toParent: self)
        views.append(hostingVC.view)
    }
}

func loadDemoData() {
    for i in 0..<3 {
        let view = demoView(contentText: "Page \(String(i))")
        loadSwiftUIViews([view])
    }
}
```

これで、ユーザーがスクロールできる異なるページを持つビューコントローラーを取得できます。

<img width="300" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/a4d23bff-b661-1fbc-5495-5bc18703cc6c.gif">


## UIPageControl のカスタムインジケーター画像を設定

全てのインジケーター画像を同じ画像に設定できます

<img width="500" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/2ad1f0a8-4e4b-4f84-f9d9-0658c55b5799.png">


```swift
pageControl.preferredIndicatorImage = UIImage(systemName: "star.fill")
```

または、ページ毎に異なる画像を設定できます

<img width="500" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/ae652c4d-84e1-fc54-7129-2b2175b8ce44.png">

```swift
pageControl.setIndicatorImage(UIImage(systemName: "sun.max.fill"), forPage: 0)
pageControl.setIndicatorImage(UIImage(systemName: "cloud.sun.fill"), forPage: 1)
pageControl.setIndicatorImage(UIImage(systemName: "cloud.drizzle.fill"), forPage: 2)
```

## UIPageControl の背景を常に見えるように設定

背景を常に非表示にすることができます。

<img width="500" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/f1cb5f04-8501-2a87-0a85-6178968030f4.png">


```swift
pageControl.backgroundStyle = .minimal
```

または、背景を常に表示することができます

<img width="500" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/ae652c4d-84e1-fc54-7129-2b2175b8ce44.png">

```swift
pageControl.backgroundStyle = .prominent
```

## UIPageControl を押してスクロールすることでページを切り替えることをユーザーに許可

通常、ユーザーが UIPageControl の上をスクロールしても、ユーザーはページを切り替えることができません:

<img width="500" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/0cc44763-5aa1-25d8-b1c7-03fe8779b349.gif">

プログラムの変数を設定することで、UIPageControlの上をスクロールすることでユーザーはページを切り替えることができます:

```swift
pageControl.allowsContinuousInteraction = true
```

<img width="500" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/e78cd859-1737-ff01-53b1-34b5c8691048.gif">

こちら [Github で完了したプロジェクト](https://github.com/mszmagic/PageViewExample)を見ることができます

---

:relaxed: [Twitter @MszPro](https://twitter.com/MszPro)

:sunny: [私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。](http://mszpro.com/ioskiji/)
<img width="150" src="https://cdn.mszmagic.com/static-web-content/AppClipImage_small.png">