[Swift入門] CollectionViewで画像スライドショーを作ってみた

Collection Viewを使って手動でも動かせるスライドショーを作ってみました。
2021.01.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

今月からCX事業本部アプリチームに配属されましたジョンハウンです?

これからiOSアプリエンジニアとして働くことが決まったものの、かなり長い間Xcodeを使ってなかったのでハマりまくっているこの頃です。半年前ぐらいにSwiftブログを書いた後、全く触ってなかった自分が少し恥ずかしくもあります。

(しかも、いつの間にかXcodeとSwiftバージョンが変わってしまってる...)

なので、今月からは本格的にSwiftとXcodeの勉強をやりながらブログを書き進めていこうと思います。

両方できるスライドショーが作りたい

手動だけor自動だけでいいんじゃない?と思うところではありますが、普段アプリを使っている時には意外と両方できるものをよく見かけます。(アプリメイン画面の上部にある広告バナーが代表的なものかも)

私もそういう優れもの作りたい!ということで参考できる資料を基に試してみました。(相変わらずのヒツジちゃん)

 

スライドショーなのでSlideViewも使えますが、今回はCollectionViewを使います。

手動でスライドさせてみる

まず、手動でスライドできる仕組みを実装してみます。

Xcodeを実行させ、新しいプロジェクトを作成します。インターフェースは前回と同じく「Storyboard」で開発を進めます。

準備

今回は画像スライドショーを作るということで、予めスライドショーに入る画像を準備します。

Assets.xcassetsというフォルダに画像をドラッグしてプロジェクト内に画像を入れておきます。画像の数はご自由に決めていただいて構いません。

StoryboardにViewを追加

Collection ViewをLibraryから追加します。

Collection Viewを追加したら三階層に分かれていることがわかります。

一番下のContent Viewに中身が入るようになるため、Content Viewの下にImage Viewを追加します。

追加したら、Collection ViewやImage ViewのConstraintsを設定して配置や大きさなどを調整します。

あと、右側の「Show the Attributes inspector」をクリックし、Scroll DirectionをHorizontalに変更します。ScrollingとPagingもEnabledに変更しておきましょう。手動でスライドするためには欠かせないものです。

Cell用のClassの作成

Collection ViewはCellという要素で構成されており、このCellの中身はカスタムで構成することが可能です。今回はImageViewのみ配置してますが、Labelなどの他のViewとも組み合わせて一つのCellとして扱うことができるのです。

そして、Cellの中身の処理をClassファイルを別途作成して行う必要があります。では早速新しいClassファイルを作成しましょう。名前はSliderCellにします。

ファイルを作成したら、Collection View Cellを選択して名前をSlider Cellに変更します。次に、右側のShow the Identity inspectorを選択して先ほど作成したClassを選択します。

Storyboardでの作業が終わりましたら、いよいよコードの作成です。

ImageViewのアウトレットを右クリックでドラグアンドドロップします。

コードは以下のように作成します。

import UIKit

class SliderCell: UICollectionViewCell {
  @IBOutlet weak var imgView: UIImageView!
  
  var image: UIImage! {
    didSet {
      imgView.image = image
    }
  }
}

View Controllerのコード作成

メイン作業であるView Controllerのコード作成です。画像のために使用する配列、Collection Viewを表示させるためのDelegateやDatasource、スクロール時に見せるページを調整する関数を設定します。

import UIKit

class ViewController: UIViewController {
  @IBOutlet private weak var collectionView: UICollectionView!
  
  let imageArr = [
    UIImage(named: "hitsuji-hai")!,
    UIImage(named: "hitsuji-homeru")!,
    UIImage(named: "hitsuji-okoru")!
  ]
  
  var currentIndex = 0

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    collectionView.delegate = self
    collectionView.dataSource = self
  }

}

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return imageArr.count
  }
  
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SliderCell", for: indexPath) as! SliderCell
    cell.image = imageArr[indexPath.item]
    return cell
  }
  
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
  }
  
  func scrollViewDidScroll(_ scrollView: UIScrollView) {
    currentIndex = Int(scrollView.contentOffset.x / collectionView.frame.size.width)
  }
}

ここまで作成ができたら、手動でのスライドは問題なくできるはずです。

自動でスライドさせる

手動でのスライドができたところで、自動でスライドする機能を実装します。

Page Control

自動スライド機能に加え、スライドショーで現在何番目の画像を見ているかを表示するPage Controlを追加します。Libararyから検索してCollection Viewの下側に配置します。

Timer

自動スライドはTimerという関数を使います。設定した時間が経つと設定したアクションを行うカラクリなのです。

では、Timer変数を宣言し、以下のコードをviewDidLoad関数の下に書きましょう。

 func startTimer() {
    timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
  }
  
  @objc func timerAction(){
    let ScrollPosition = (currentIndex < imageArr.count - 1) ? currentIndex + 1 : 0
    collectionView.scrollToItem(at: IndexPath(item: ScrollPosition, section: 0), at: .centeredHorizontally, animated: true)
  }

最後に、viewDidLoad関数の中にstartTimer()を書き込めば完成です。

View Controller全体のコードは以下のようになります。

import UIKit

class ViewController: UIViewController {
  
  @IBOutlet private weak var collectionView: UICollectionView!
  @IBOutlet private weak var pageControl: UIPageControl!
  
  let imageArr = [
    UIImage(named: "hitsuji-hai")!,
    UIImage(named: "hitsuji-homeru")!,
    UIImage(named: "hitsuji-okoru")!
  ]
  
  var currentIndex = 0
  var timer : Timer?
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    collectionView.delegate = self
    collectionView.dataSource = self
    pageControl.numberOfPages = imageArr.count
    
    startTimer()
  }
  
  func startTimer() {
    timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
  }
  
  @objc func timerAction(){
    let ScrollPosition = (currentIndex < imageArr.count - 1) ? currentIndex + 1 : 0 collectionView.scrollToItem(at: IndexPath(item: ScrollPosition, section: 0), at: .centeredHorizontally, animated: true) } } extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return imageArr.count
  }
  
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SliderCell", for: indexPath) as! SliderCell
    cell.image = imageArr[indexPath.item]
    return cell
  }
  
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
  }
  
  func scrollViewDidScroll(_ scrollView: UIScrollView) {
    currentIndex = Int(scrollView.contentOffset.x / collectionView.frame.size.width)
    pageControl.currentPage = currentIndex
  }
}

最後に

手動と自動同時にできるスライドショーができて、楽しかったです。ただ、手動の場合に最初の画像から最後の画像に飛んだり、最後の画像から最初の画像に飛ぶような無限ループ形式にはできなかったのが惜しかったです。

やはり、まだまだ勉強が必要だと感じますし、また後程このコードは修正して完成版をまた公開したいなと思います。

参考

iOS Image Slider Using CollectionView