はじめに
こんにちは、nukkyです。
スライドショーやウォークスルーなどで何かしらのページングビューを使用するかとは思いますが、お手軽に使えるUIScrollViewだとループができないので、今回は自作でループができるように作ってみました!
前提条件
Xcode 9.x
iOS 11 Simulator
Swift 4.0
ページングビューとは
スクロールと違い任意の位置で止まるようなビューではなく、1ページ単位で切り替わるビューのことです、
今回作成する画面の完成イメージはこのようになります。
実装
Storyboardの準備
まずはStoryboardにUIScrollViewを用意します。
そうしたら、UIScrollViewの「indicators」と「Scrolling」を画像のように設定してください。
今回はページングなのでindicatorの表示はなくしています、それと忘れずに「Paging Enabled」にはチェックを入れてください。
それではこのUIScrollViewをViewControllerにリレーションしてください。
1 | @IBOutlet weak var scrollView: UIScrollView! |
コードの実装
まずは今回使用する変数を宣言しておきます。
1 2 3 4 5 6 7 8 9 10 | // ページビューのラベルのタグ static let LABEL_TAG = 100 // ページビューの背景色 private let colorArray: [UIColor] = [.red,.green,.blue,.yellow,.purple] // 現在表示されているページ private var page: Int = 0 // ScrollViewをスクロールする前の位置 private var startPoint: CGPoint! // 表示するページビューの配列 private var pageViewArray: [UIView] = [] |
viewDidLoadでscrollViewのdelegateを設定します。
1 2 3 4 | override func viewDidLoad() { super.viewDidLoad() scrollView.delegate = self } |
scrollViewに各ページのビューを生成します、オートレイアウトが適用されているviewDidAppear後が望ましいです、今回はviewDidAppear内で生成を行っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // scrollViewの表示サイズ let size = CGSize(width: scrollView.frame.size.width, height: scrollView.frame.size.height) // 5ページ分のcontentSize let contentRect = CGRect(x: 0, y: 0, width: size.width * CGFloat(5), height: size.height) let contentView = UIView(frame: contentRect) for i in 0..<5 { let pageView = UIView(frame: CGRect(x: size.width * CGFloat(i), y: 0, width: size.width, height: size.height)) pageView.backgroundColor = colorArray[i] let label = UILabel() label.text = "View_\(i)" label.backgroundColor = .white label.sizeToFit() label.center = pageView.center label.frame.origin.x = 10 // あとで使いたいのでtagを設定 label.tag = ViewController.LABEL_TAG pageView.addSubview(label) contentView.addSubview(pageView) // あとで再描画をできるように保持 pageViewArray.append(pageView) } // scrollViewに5ページ分のViewとサイズを設定する scrollView.addSubview(contentView) scrollView.contentSize = contentView.frame.size // 5つの真ん中のViewを初期位置に変更 scrollView.contentOffset = CGPoint(x: ((size.width * 2)), y: 0) startPoint = scrollView.contentOffset; // 最初に表示するページ page = 0 // 各ページの再描画 setPageView() } // 各ページの再描画 func setPageView() { for i in 0..<pageViewArray.count { // 5つあるビューの真ん中が現在選択されているページになるようにする let index = getPageIndex(page: page + (i - 2)) pageViewArray[i].backgroundColor = colorArray[index] // tagからラベルを取得しtextを再設定 let label: UILabel = pageViewArray[i].viewWithTag(ViewController.LABEL_TAG) as! UILabel label.text = "View_\(index)" label.sizeToFit() } } // ページがpageViewArray.count以上や0以下になった時に適切な値を返す func getPageIndex(page: Int) -> Int { var index = page if index < 0 { index = (pageViewArray.count + page) } else if index >= pageViewArray.count { index = page - pageViewArray.count } return index } |
ループの肝となるUIScrollViewDelegateを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | extension ViewController: UIScrollViewDelegate { func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { // 左右のスワイプを判断するのでスクロール開始時に設定 self.startPoint = scrollView.contentOffset; } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView){ if startPoint.x > scrollView.contentOffset.x {//左スワイプ pageChange(num: -1) } else if startPoint.x < scrollView.contentOffset.x {//右スワイプ pageChange(num: 1) } // scrollViewのスクロール位置を真ん中のビューに戻す let point = CGPoint(x: ((scrollView.frame.size.width * 2)), y: 0) scrollView.setContentOffset(point, animated: false) // pageが切り替わったのでビューを再描画 setPageView() } // ページがループできるように適切な値をセットする private func pageChange(num:Int) { if page + num < 0 { page = pageViewArray.count - 1 } else if page + num >= pageViewArray.count { page = 0 } else { page = page + num } } } |
仕組み
駆け足でコードを貼ってしまいましたが、実際どのようにしてループさせているかというと以下のような流れです。
- まず5つ分のページのビューを用意します。
- このビューをUIScrollViewにaddSubViewします。
- 真ん中のビューが表示されるように位置を調整します。
- 真ん中のビューが初期のページになるように「1」のビューを更新します。
- UIScrollViewDelegateでスクロール終了を取得します。
- スクロール終了したら真ん中のビューが表示されるように位置を戻します。
- 「1」で用意したビューを更新して現在のページが真ん中に表示されるようにします。
「1」で用意した時のページの並びが[0,1,2,3,4]とした時に「4」のタイミングで0ページ目を初期位置にしたい場合ページの並びは[3,4,0,1,2]になります。
上記の状態で左スワイプをおこない「5」のタイミングで1ページ目が表示された場合、「7」のタイミングでページの並びを[4,0,1,2,3]に変更しビューの更新を行い、常に真ん中のビューに現在のページをセットすることで、ページングしているように見せつつループして表示することができます。
さいごに
UIScrollViewでのページングのループ処理どうでしょうか、今回は5つでサンプルを書きましたがViewの数は5つのままでページの要素を増やしていけば、メモリはView5つ分のままでまだまだページを増やすことも可能です!結構便利にできているのではないかと思うので同じようなものが欲しい方の力になれれば嬉しいです!
RE:ENGINESブログ「iOS記事」まとめページはこちらから