はじめに
よくあるような縦方向・横方向の ScrollView の実装方法のメモ。
以下のような画面を実装します。
よければ参考にしてください。
【注意】
※ Viewはコードベースで作成しています。
※ レイアウトには SnapKit を使用しています。
環境
- Swift5
- Xcode13.0
実装手順
順番に実装手順を記載していきます。
ここでは、色々省略して、最低限のコードのみ記載しています。
細かいことは、記事の最後に全体のコードを載せておりますので、そちらをご参照ください。
① ScrollView を生成する。
private let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.isPagingEnabled = false
scrollView.showsVerticalScrollIndicator = true // 縦方向のスクロールバーをつけるかどうか。
// !! 横方向のスクロールバーは以下で設定する。
// scrollView.showsHorizontalScrollIndicator = true
return scrollView
}()
② ScrollView 内でスクロールさせるViewを生成する。(本記事では、contentView とする)
private let contentView = UIView()
③ 実際に表示したい各Viewを生成していく。(本記事では、赤色のView, 青色のView, 黄色のView が該当)
private let contentView: UIView = {
let view = UIView()
return view
}()
private let redView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
private let blueView: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
④ contentsView に、③で生成した View を追加して、AutoLayoutをかけていきます。
縦方向のスクロールを前提に、レイアウトをかけていきます。
contentView.addSubview(redView)
contentView.addSubview(blueView)
contentView.addSubview(yellowView)
redView.snp.makeConstraints {
$0.top.left.right.equalToSuperview()
$0.height.equalTo(view.frame.height)
}
blueView.snp.makeConstraints {
$0.top.equalTo(redView.snp.bottom)
$0.left.right.equalToSuperview()
$0.height.equalTo(view.frame.height)
}
yellowView.snp.makeConstraints {
$0.top.equalTo(blueView.snp.bottom)
$0.left.right.bottom.equalToSuperview()
$0.height.equalTo(view.frame.height)
}
⑤ scrollView に contentsView を追加して、AutoLayoutをかけていきます。
scrollView.addSubview(contentView)
contentView.snp.makeConstraints {
$0.width.equalTo(scrollView.frameLayoutGuide) // 縦方向にスクロールさせたい場合
// !! 横方向にスクロールしたい場合は、height に対して制約をかけます。
// $0.height.equalTo(scrollView.frameLayoutGuide)
$0.edges.equalTo(scrollView.contentLayoutGuide)
}
⑥ ViewController の View に ScrollView を追加して、AutoLayoutをかければ、最小限の実装は完了します。
view.addSubview(scrollView)
scrollView.snp.makeConstraints {
$0.edges.equalTo(view.safeAreaLayoutGuide)
}
⑦ あとは適宜、調整する。
その他
横スクロールの際の、PageControl は以下で実装します。
(全体のコードを最後に記載しているのでそちらも合わせてご参照ください。)
① PageControl を生成する。
private let pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.pageIndicatorTintColor = .lightGray
pageControl.currentPageIndicatorTintColor = .black
pageControl.numberOfPages = 3 // ページ数。(画面でいうドットの数。)
return pageControl
}()
② ScrollView の delegete を ViewController に設定する。
private lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.delegate = self
return scrollView
}()
③ scrollViewDidScroll(_ scrollView:) の delegateメゾットから UIPageControlを更新する。
extension HorizontalViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
pageControl.currentPage = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
}
}
全体のコード
以下に、「はじめに」に添付した Gif のコードを記載します。
GitHubはこちら。
縦方向のスクロール
import UIKit
import SnapKit
final class VerticalViewController: UIViewController {
private let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.backgroundColor = .red
return scrollView
}()
private let contentView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
return view
}()
private let redView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
private let blueView: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
private let yellowView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
return view
}()
override func viewDidLoad() {
contentView.addSubview(redView)
contentView.addSubview(blueView)
contentView.addSubview(yellowView)
scrollView.addSubview(contentView)
view.addSubview(scrollView)
redView.snp.makeConstraints {
$0.top.left.right.equalToSuperview()
$0.height.equalTo(view.frame.height)
}
blueView.snp.makeConstraints {
$0.top.equalTo(redView.snp.bottom)
$0.left.right.equalToSuperview()
$0.height.equalTo(view.frame.height)
}
yellowView.snp.makeConstraints {
$0.top.equalTo(blueView.snp.bottom)
$0.left.right.bottom.equalToSuperview()
$0.height.equalTo(view.frame.height)
}
contentView.snp.makeConstraints {
$0.width.equalTo(scrollView.frameLayoutGuide) // 縦方向の Scroll の場合は Width に合わせる
$0.edges.equalTo(scrollView.contentLayoutGuide)
}
scrollView.snp.makeConstraints {
$0.edges.equalTo(view.safeAreaLayoutGuide)
}
}
}
横方向のスクロール(チュートリアル画面風)
import UIKit
import SnapKit
final class HorizontalViewController: UIViewController {
private let headerLabel: UILabel = {
let label = UILabel()
label.text = "横方向の ScrollView\n(チュートリアル画面風)"
label.numberOfLines = 0
label.font = .systemFont(ofSize: 20)
label.textAlignment = .center
label.textColor = .black
return label
}()
private lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
return scrollView
}()
private let contentView: UIView = {
let view = UIView()
return view
}()
private let redView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
private let blueView: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
private let yellowView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
return view
}()
private let pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.pageIndicatorTintColor = .lightGray
pageControl.currentPageIndicatorTintColor = .black
pageControl.numberOfPages = 3
return pageControl
}()
override func viewDidLoad() {
contentView.addSubview(redView)
contentView.addSubview(blueView)
contentView.addSubview(yellowView)
scrollView.addSubview(contentView)
view.addSubview(headerLabel)
view.addSubview(scrollView)
view.addSubview(pageControl)
redView.snp.makeConstraints {
$0.top.bottom.left.equalToSuperview()
$0.width.equalTo(view.frame.width)
}
blueView.snp.makeConstraints {
$0.top.bottom.equalToSuperview()
$0.left.equalTo(redView.snp.right)
$0.right.equalTo(yellowView.snp.left)
$0.width.equalTo(view.frame.width)
}
yellowView.snp.makeConstraints {
$0.top.bottom.right.equalToSuperview()
$0.left.equalTo(blueView.snp.right)
$0.width.equalTo(view.frame.width)
}
contentView.snp.makeConstraints {
$0.height.equalTo(scrollView.frameLayoutGuide) // 横方向の Scroll の場合は height を frameLayoutGuide に合わせる
$0.edges.equalTo(scrollView.contentLayoutGuide)
}
headerLabel.snp.makeConstraints {
$0.top.left.right.equalTo(view.safeAreaLayoutGuide)
$0.height.equalTo(100)
}
scrollView.snp.makeConstraints {
$0.top.equalTo(headerLabel.snp.bottom)
$0.left.right.equalTo(view.safeAreaLayoutGuide)
$0.bottom.equalTo(pageControl.snp.top)
}
pageControl.snp.makeConstraints {
$0.top.equalTo(scrollView.snp.bottom)
$0.left.right.bottom.equalTo(view.safeAreaLayoutGuide)
$0.height.equalTo(15)
}
}
}
extension HorizontalViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
pageControl.currentPage = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
}
}