iPhone
 Computer >> コンピューター >  >> スマートフォン >> iPhone

AutoLayoutとSwiftで構築するiOS向けSpotifyクローンの作り方

この記事では、Swiftを使ってSpotifyのホーム画面レイアウトをプログラムで再現する方法を解説します。なぜストーリーボードではなくプログラムで構築するのでしょうか?それは、複数のアプローチでUIを構築できるスキルは常に価値があり、コードで実装するスタイルが好きだからです。特にチーム開発やバージョン管理システムを使用している場合、このスキルは大いに役立ちます。

以下はSpotifyモバイルアプリの実際のホーム画面です。このようなレイアウトを実現するためにUICollectionViewを使用し、タブナビゲーションの作成にはTabBarControllerも活用します。

基本要件:まず、Xcode 10以上とSwift 4以上がインストールされていることを確認してください。

プロジェクトのセットアップ

Xcodeで新しいプロジェクトを作成することから始めましょう:

ViewController.swiftで最初に行うべきは、スーパークラスをUIViewControllerからUICollectionViewControllerへ変更することです。今回のクラスはcollectionViewをベースとするためです。

//
//  ViewController.swift
//  spotifyAutoLayout
//
//  Created by admin on 10/31/19.
//  Copyright © 2019 Said Hayani. All rights reserved.
//

import UIKit

class ViewController: UICollectionViewController {

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


}

この状態でアプリを実行するとビルドに失敗します。AppDelegate.swiftファイル内のdidFinishLaunchingWithOptions関数にあるreturn文の前に、次のコードを追加する必要があります:

  let layout = UICollectionViewFlowLayout()
        window = UIWindow()
        window?.rootViewController = ViewController(collectionViewLayout: layout)

コード全体はこのようになります:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        let layout = UICollectionViewFlowLayout()
        window = UIWindow()
        window?.rootViewController = ViewController(collectionViewLayout: layout)
        return true
    }

これでアプリを実行すると、背景色がpurple(紫)に変わっているのが確認できます:

CollectionViewのメソッドを定義する

次のステップは、レイアウトを整理し、セクション間でスペースを均等に分割することです。

CollectionViewのメソッドを定義していきます。手順は以下の通りです:

  • 一意の識別子で再利用可能なセルを登録する
  • セクション内のアイテム数を定義する
  • 登録したセルを使用する

CollectionViewの一部のメソッドを使用するには、UICollectionViewDelegateFlowLayoutに準拠する必要があります。そうすることでメソッドの自動補完も利用できます。それでは、CollectionViewCellの登録から始めましょう。

viewDidLoad()内でcollectionView.register()メソッドを呼び出し、再利用可能なセルを登録します:

  collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

次に、numberOfItemsInSectionを使ってcollectionView内のセル数を定義します。今はとりあえず5個にしておきましょう:

 override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }

続いて、cellForItemAtで再利用可能なセルを定義します。このメソッドはUICollectionViewCellを返し、cellIdという一意のIDを持ちます。コードは以下のようになります:

 override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
        cell.backgroundColor = .red
        return cell
    }

完全なコードは次のようになります:

import UIKit

class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    let cellId : String = "cellId"

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.backgroundColor = .purple
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

    }


    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
        cell.backgroundColor = .red
        return cell
    }

}

これで、赤い背景の5つのアイテムが画面に表示されるはずです:

セルにカスタムの幅と高さを設定する

次に、セルを正しい順序で配置し、width(幅)とheight(高さ)を設定します。各セルは画面の幅をそのまま使用します。

幸いsizeForItemAtメソッドがあるため、セルにカスタムの幅と高さを設定できます。このメソッドはCGSize型を返す必要があります:

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = view.frame.width
        let height = CGFloat(200)
        
        return CGSize(width: width, height: height)
    }

ここでは、view.frame.widthを使ってセルに画面の幅を設定し、高さにはCGFloat型のカスタム値を指定しました。

シミュレータで結果を確認してみてください:

ここまでは順調です。次は、再利用可能なカスタムセルを作成しましょう。CustomCellという名前の新しいSwiftファイルを作成します:

CustomCell.swiftは以下のようになります:


import UIKit

class CustomCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super.init(frame: frame)
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

次に、再利用可能なセルをサポートするために2つのメソッド、collectionView.registercellForItemAtを修正します。まずregisterメソッドから。UICollectionViewCell.selfCustomCellに置き換えます:

 collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

続いて、cellForItemAtCustomCellにキャストします:

  let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell

このままでは変化がわかりにくいため、CustomCellに背景色backgroundColor = .yellowを設定しましょう。cellForItemAt内のcell.backgroundColor = .redの行を削除することを忘れずに。背景色が黄色に変われば成功です。

さて、いよいよCustomCellに本格的な機能を加えていきましょう!

Spotifyのホーム画面を見ると、各セクション(この例ではCustomCell)にはセクションタイトルとサブセルが含まれており、横方向にスクロールする仕組みになっています:

セクションタイトルを追加する

セルにタイトルラベルを追加しましょう。CustomCellクラス内にtitleLabel要素を作成します:

let titleLabel: UILabel = {
        let lb  = UILabel()
        lb.text = "Section Title"
        lb.font = UIFont.boldSystemFont(ofSize: 14)
        lb.font = UIFont.boldSystemFont(ofSize: 14)
        
        return lb
    }()

次に、init()ブロック内でこの要素をビューに追加します:

addSubview(titleLabel)

この状態でアプリを実行しても変化は見られません。それは要素にまだ制約を設定していないからです。そこで制約を追加しましょう。titleLabellb.translatesAutoresizingMaskIntoConstraints = falseプロパティを追加して、制約を適用できるようにします。

titleLabelをビューに追加した後、制約を定義します:

 addSubview(titleLabel)
titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = truetitleLabel.leftAnchor.constraint(equalTo: leftAnchor,constant: 8 ).isActive = true

必ず.isActive = trueプロパティを付けてください。これがないと制約が機能しません!

次のステップに進む前に、画面の背景色を黒に変更し、セルの黄色も削除しておきましょう:

サブセルを追加する

さて、ここからが重要なパートです。各セルの中にサブセルを配置していきます。そのために、CustomCellの中にCollectionViewを追加します。

UICollectionViewCellの中にCollectionViewを追加するには、UICollectionViewDelegateUICollectionViewDelegateFlowLayout、そしてUICollectionViewDataSourceの各プロトコルをCustomCellに準拠させる必要があります。

通常のビューと同じ要領で、collectionView要素を作成しましょう:


    let collectionView : UICollectionView = {
        // init the layout
        let layout = UICollectionViewFlowLayout()
        // set the direction to be horizontal
        layout.scrollDirection = .horizontal
        
        // the instance of collectionView
        
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
       
        // Activate constaints
      
        cv.translatesAutoresizingMaskIntoConstraints = false
        
        return cv
        
    }()
参考:https://stackoverflow.com/questions/44614743/add-uicollectionview-in-uicollectionviewcell

最初にviewController.swiftで行ったのと同様に、イニシャライザでlayoutcollectionViewに渡している点に注目してください。ここではさらに、FlowLayoutのスクロール方向を.horizontalに指定しています。

collectionView要素をサブビューとしてビューに追加しましょう。コードを整理するために、専用の関数を作成します:

    fileprivate  func setupSubCells(){
        // add collectionView to the view
        addSubview(collectionView)
 
        collectionView.dataSource = self
        collectionView.delegate = self
        // setup constrainst
        // make it fit all the space of the CustomCell
        collectionView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true
        collectionView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        collectionView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    }

collectionViewのdelegateとdataSourceを必ずselfに設定してください:

collectionView.dataSource = self

collectionView.delegate = self

その後、initブロック内でこの関数を呼び出します。

この状態でビルドすると、UICollectionViewDelegateUICollectionViewDelegateFlowLayoutプロトコルに準拠していないため、Xcodeがエラーを表示します。これを解決するには、まずサブセルを再利用可能なセルとして登録する必要があります。

クラスの先頭にcellIdという名前の変数を作成します。セル識別子が必要なときに使用できます:

let cellId : String = "subCellID"

collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

エラーを解消するには、あと2つのメソッドが必要です。セクション内のセル数を定義するnumberOfItemsInSectionと、再利用可能なセルを定義するcellForItemAtです。これらはcollectionViewが正常に動作するために不可欠なメソッドです:

 // number of cells
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
       return  4
    }
    
    // reusable Cell
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
         cell.backgroundColor = .yellow
        
        return cell
    }

結果はこのようになります:

ご覧のとおり、collectionViewの背景は紫で、サブセルは黄色で表示されています。

最後に、subCellsの高さをセクションの高さに合わせ、幅も同様に設定しましょう。ここでもsizeForItemAtを使用してセルのheightwidthを定義します。

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        let width = frame.height
        let height = frame.height
        
        return CGSize(width: width, height: height)
        
    }

そして完成です!

素晴らしい出来栄えですね!この記事が長くなりすぎないよう、ここで一旦区切ります。第2部では、モック画像を追加して実際のデータを入力していきます。

完全なソースコードはこちら

追加情報、質問、訂正などがあれば、ぜひ下のコメント欄に投稿するか、Twitterでお気軽にご連絡ください。

このチュートリアルの第2部が公開された際に通知を受け取るには、メールリストに購読登録してください。

  1. iOS 13でBluetoothマウスをiPhoneに接続する方法【AssistiveTouch活用ガイド】

    iOS 13およびiPadOSへのアップデートにより、ついにBluetoothマウスをiPhoneやiPadに接続できるようになりました。アクセシビリティ機能を活用すれば、iPhoneにマウスを接続して、デスクトップPCのように快適にインターネットを閲覧したり、ドキュメントを素早く操作したりすることが可能です。 iPhoneでマウスを使う方法 オフィスでの会議中にプロジェクターと組み合わせてiPhoneを操作したい場合など、マウスがあると非常に便利な場面は多くあります。iOS 13.3ではAssistiveTouch(アシスティブタッチ)機能に変更が加えられ、この優れた機能を使ってiPhone

  2. iOS 16搭載iPhoneでバッテリー残量(%)を常に表示する方法

    5年前、AppleはiPhone Xを発売しました。画面上部に賛否両論を呼んだ「ノッチ」を備えた、かなり先進的なデザインが特徴でした。見た目のインパクトはさておき、ノッチの導入によりステータスバーに表示できるアイコンや情報のスペースは大幅に狭まりました。その影響の一つとして、Appleはバッテリー残量(%)の常時表示オプションを廃止してしまったのです。画面右上から下にスワイプしてコントロールセンターを開けば、正確なバッテリー残量を確認することはできます(現在も可能です)。しかし、この方法やウィジェットを活用した他の回避策は、すべてのユーザーにとって満足のいくものではありませんでした。嬉しいこと