Help us understand the problem. What is going on with this article?

【徹底解説】UIScrollViewクラス その1

More than 1 year has passed since last update.

UIScrollViewクラスとは

UIScrollView(以下,SV)は,画面に触れた指などの動きを追跡することで,画面サイズよりもはみ出たコンテンツを表示させるためのUI部品です。 UITableViewやUITextViewも画面からはみ出た範囲をスクロールさせることができますが,その理由は,SVがこれらのスーパークラスであるためです。

環境

  • XCode Version 10.2.1
  • Swift 5.0

第1章 とりあえずスクロールさせる

ここではスクロールするコンテンツをstoryboardを用いて作製してみます。実践には程遠いですが何事も基本が大切なので基礎固めしていきましょう。でも本当に固まってはいけません。

SVの配置

ナビゲータエリアからMain.storyboardを選択し,Objectライブラリから「Scroll View」を選びます。それを画面(View)上にドロップします。ドロップしたSVを画面全体に広げます。もちろん必ずしも画面全体に広げる必要はありません。用途に応じてサイズを変更させてください。ここでは練習のため画面全体に広げているだけです。

ScrollView_1.png

コンテンツビューの配置

実際にスクロールさせる部品であるコンテンツビュー(contents view)を配置します。コンテンツビューにはUIViewを用いることが多いと思います。Objectライブラリから「View」を選択し,SV上にドロップします。以後分かりやすいようにViewの名前を「contentsView」に変更します。

大きさと位置を決めましょう。右上にある「Show the Size inspector」にある「width」と「height」にぞれぞれ「800」と「1200」を設定します。このcontentsViewを上下左右に青い点線が出るところに配置します。もし上手くいかなければ,「Show the Size inspector」の「X」と「Y」にそれぞれ「-193」と「-152」を入力してください(iPhoneXRを選択していること)。もちろんこれらも用途に合わせて自由に設定してください。

コンテンツビューだけではスクロールしているかどうかの判別が付かないのでUILabelを配置させましょう。Objectライブラリから「label」を選び,contentsView上にドロップさせ,上下左右に青い点線が出るところに配置させます。

ScrollView2.png

コンテンツビューのレイアウト

まだ各UI部品の位置関係についても何も制約をかけていませんでしたので制約をかけていきます。View Controllerを選択してある状態で画面右下の「Resolve Auto Layout issues」を押します。その中にある「All Views in Container」欄の「Add Missing Constraints」を押します。すると下図のように制約がかかると思います。

ScrollView_3.png

お疲れさまです。ここまできたら一旦ビルドしてみて下さい。ビルド後,画面をマウスなどで動かして見てください。おそらく動かないはずです。衝撃的ですよね。ここまで説明しておいて動かないなんて。SV上にはそれより大きなcontentsViewを配置したのでスクロールできても良さそうですが何故かできせん。Appleが悪いわけではありません。制約のかけ方が間違っているからです。下図の青色の点線で囲んだところをよく見てください。

ScrollView_4.png

例えばSVのbottomの位置をみるとcontentsViewから152下がった位置で制約がかかっています。上下左右ともSVとcontentsViewの位置はガチガチに制約がかかっている状態です。この状態ではスクロールはできません。

それではスクロールができるように制約をかけ直しましょう。まず上の青い点線で囲んだ「Constraints」を消します。次に,contentsViewを選択し,画面右下にある「Add New Constraints」を押し,4つとも「0」を入力します。そして最後に「Add 4 Constraints」を押し,ビルドしてください。今度はスクロールできるはずです。

ScrollView_1.gif

スクロールできた理由が大切です。SVの上下左右の位置とcontentsViewの上下左右の位置を一致させたためです。これによって,スクロールできる範囲は,左方向に関してはcontentsViewの左端がSVの左端に一致するまでとなりました。その他の位置も同様です。

第2章 コードでやってみる

SVの配置のみstoryboard上で行い,上記で行ったコンテンツビューの配置などはコードでやってみましょう。

Outlet接続

SV以外のUIは全て削除し,SVをscrollViewという名前でOutlet接続させます。

ScrollView_6.png

コンテンツビューの作製

コンテンツビューとその中身を作っていきましょう。以下がコード全体になります。

ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        configureSV()
    }

    func createLabel(contentsView: UIView) -> UILabel {

        // labelを作る
        let label = UILabel()

        // labelの座標をcontentsViewの中心にする
        let labelX = contentsView.center.x
        let labelY = contentsView.center.y
        label.frame = CGRect(x: labelX, y: labelY, width: 95, height: 50)

        label.text = "Label"
        return label
    }

    func createContentsView() -> UIView {

        // contentsViewを作る
        let contentsView = UIView()
        contentsView.frame = CGRect(x: 0, y: 0, width: 800, height: 1200)

        // contentsViewにlabelを配置させる
        let label = createLabel(contentsView: contentsView)
        contentsView.addSubview(label)

        return contentsView
    }

    func configureSV() {

        // scrollViewにcontentsViewを配置させる
        let subView = createContentsView()
        scrollView.addSubview(subView)

        // scrollViewにcontentsViewのサイズを教える
        scrollView.contentSize = subView.frame.size
    }

}

それでは細かく見ていきましょう。

func createLabel(contentsView: UIView) -> UILabel

ラベルを生成するメソッドです。

let label = UILabel()
// labelの座標をcontentsViewの中心にする
let labelX = contentsView.center.x
let labelY = contentsView.center.y
label.frame = CGRect(x: labelX, y: labelY, width: 95, height: 50)
label.text = "Label"
return label

引数であるcontentsViewの中心にlabelが配置するように設定しています。returnでそのlabelを返します。

func createContentsView() -> UIView

コンテンツビューを生成するメソッドです。

let contentsView = UIView()
contentsView.frame = CGRect(x: 0, y: 0, width: 800, height: 1200)
// contentsViewにlabelを配置させる
let label = createLabel(contentsView: contentsView)
contentsView.addSubview(label)
return contentsView

func configureSV()

SVの設定を行うメソッドです。

// scrollViewにcontentsViewを配置させる
let subView = createContentsView()
scrollView.addSubview(subView)
// scrollViewにcontentsViewのサイズを教える
scrollView.contentSize = subView.frame.size

スクロールさせるためにはSVにコンテンツビューの大きさを教える必要があります。SVのcontentSizeプロパティーにその大きさを設定することでSVにコンテンツビューの大きさを教えることができます。

これでビルドすると第1章と同様にスクロールさせることができると思います。ただ現状では,初期の「label」の位置が右下付近によっています。上下左右の中央にずらしたいですが,初期位置に関しては別の章で説明します。

第3章 横スクロールさせる

第1,2章だけではあまり面白みがありません。SVを使用する時は,横か縦方向にスクロールさせることが多いと思います。この章では横スクロールに焦点を当て,もうすこし実践的にしましょう。

スクロールで色が変わる画面を作る

次のようにスクロールさせることでページごとに色が変わるのアプリケーションを作りましょう。

Scroll_color.001.png

スクロールビューのみstoryboardを使用して,それ以外はコードで実装していきます。任意ですが,スクロールビューの制約はSafeAreaに対して行います。全体のコードです。

ViewController.swift
import UIKit

class ViewController: UIViewController {

    let numberOfPages = 5
    let colors: [UIColor] = [.yellow, .gray, .red, .blue, .brown]

    @IBOutlet weak var scrollView: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        configureSV()
    }

    func createPages(page: Int) -> UIView {
        let pageView = UIView()
        let pageSize = scrollView.frame.size
        let positionX = pageSize.width * CGFloat(page)
        let position = CGPoint(x: positionX, y: 0)
        pageView.frame = CGRect(origin: position, size: pageSize)
        pageView.backgroundColor = colors[page]

        return pageView
    }

    func createContentsView() -> UIView {
        let contentsView = UIView()
        let contentsWidth = scrollView.frame.width * CGFloat(numberOfPages)
        let contentsHeight = scrollView.frame.height
        contentsView.frame = CGRect(x: 0, y: 0, width: contentsWidth, height: contentsHeight)

        for i in 0 ..< numberOfPages {
            let pageView = createPages(page: i)
            contentsView.addSubview(pageView)
        }

        return contentsView
    }

    func configureSV() {
        let contentsView = createContentsView()
        scrollView.addSubview(contentsView)
        scrollView.contentSize = contentsView.frame.size
    }

}

細かく見ていきましょう。

インスタンスプロパティー

 numberOfPages:ページ数
 colors:色の設定

func createPages(page: Int) -> UIView

ページを生成するメソッドです。

let pageView = UIView()
let pageSize = scrollView.frame.size
let positionX = pageSize.width * CGFloat(page)
let position = CGPoint(x: positionX, y: 0)
pageView.frame = CGRect(origin: position, size: pageSize)
pageView.backgroundColor = colors[page]
return pageView

ページの幅はscrollViewと同じにしています。引数pageは現在のページの番号です。ページがスクロールした分だけページの位置がずれ,その値をpositionXに格納します。pageの背景色はcolorsに格納されている色を順に取り出して
backgroundColorで設定します。

func configureSV()

前章と同じでSVの設定を行います。

さて,これでビルドすると以下のようにスクロールができると思います。

Scroll_color.gif

スクロールビューの設定

ここではスクロールビューにどんな設定ができるかを見てみましょう。すべてBool型で設定します。

インスタンスプロパティー名 trueの時の内容 デフォルト値
isScrollEnabled スクロール可能 true
isDirectionalLockEnabled 最初にスクロールさせた方向(水平か垂直)のみスクロール可能 false
isPagingEnabled 1ページずつスクロールする false
scrollsToTop ステータスバーをタップすると一番上までスクロールする true
bounces スクロールがコンテンツの一番端に到達すると跳ね返る(バウンドする) true
alwaysBounceVertical コンテンツがスクロールビューよりも小さい場合でも常に垂直方向のバウンドを許可する。ただしbounces=trueであることも必要 false
alwaysBounceHorizontal コンテンツがスクロールビューよりも小さい場合でも常に水平方向のバウンドを許可する。ただしbounces=trueであることも必要 false

例えば,ページ単位でスクロールするには以下のように設定します。

let scrollView = UIScrollView()
scrollView.isPagingEnabled = true

記事が長くなってきたのでここで一旦区切ります。第4章以降は別記事で説明します。お疲れ様でした。

続編

【徹底解説】UIScrollViewクラス その2(執筆中)

参考文献

詳細! Swift iPhoneアプリ開発入門ノート 著者:大重 美幸 出版社: ソーテック社


ご意見等あれば連絡ください。

ynakaDream
趣味レベルでプログラムをしています。使用言語はSwiftとPythonがメインです。これらを用いてiOSアプリや人工知能のプログラムを嗜んでいます。よろしくお願いします。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした