114
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DeNAAdvent Calendar 2018

Day 5

iOSアプリでそろそろセミモーダルビューを使ってみたい人に「FloatingPanel」をおすすめする

Last updated at Posted at 2018-12-04

前段

iOSのマップアプリやミュージックアプリなどで見られる セミモーダルビュー

iPhone の大画面化により、片手操作だと画面上部の入力をスムーズに行えないという機会が多くなりました。
iPhone XiPhone XS のホームバーのように、画面下部に動線を配置して操作が行えるセミモーダルを使ったUIも普及していくのではないかと思います。

セミモーダルビューの例(iOSのマップアプリより)
test.gif

しかしながら現在(2018年11月時点)ではセミモーダルビューを手軽に実現できるコンポーネントはUIKitでは提供されておらず、実現するためにはUIScrollViewなどをカスタムして自前で実装する必要があります。

プロダクト開発でセミモーダルビューを使ってみたいなーと思った際に見つけた、手軽にセミモーダルビューを作ることができる 「FloatingPanel」 という素敵なライブラリを紹介します。

FloatingPanel を使ってセミモーダルビューを作る

1. 導入

CocoaPodsCarthage に対応しているので適宜インストールします。

// Podfile

pod 'FloatingPanel'
// Cartfile

github "scenee/FloatingPanel"

2. 基本的な使い方

2.1 セミモーダルビューとなるViewControllerを定義する

セミモーダルビューとなるViewControllerは UIViewController を継承していればなんでもOKなのですが、見た目上のわかりやすさのため背景色だけ設定しておきます。

class SemiModalViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // わかりやすくするため背景色だけ設定
        self.view.backgroundColor = UIColor.orange
    }
}

2.2 呼び出し元のViewControllerからFloatingPanelを使ってセミモーダルビューを表示する

import UIKit
import FloatingPanel

class ViewController: UIViewController {
    
    var floatingPanelController: FloatingPanelController!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        floatingPanelController = FloatingPanelController()

        // セミモーダルビューとなるViewControllerを生成し、contentViewControllerとしてセットする
        let semiModalViewController = SemiModalViewController()
        floatingPanelController.set(contentViewController: semiModalViewController)

        // セミモーダルビューを表示する
        floatingPanelController.addPanel(toParent: self, belowView: nil, animated: false)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // セミモーダルビューを非表示にする
        floatingPanelController.removePanelFromParent(animated: true)
    }
}

ここまでの基本的な使い方で👇のようなセミモーダルビューができます。簡単!

セミモーダルビュー(基本バージョン)
basic_semi_modal.gif

3. セミモーダルビューをカスタマイズする

さらに細かくカスタマイズをすることができます。

セミモーダルビューを角丸にする

floatingPanelController = FloatingPanelController()
floatingPanelController.surfaceView.cornerRadius = 24.0

セミモーダルビューの高さ位置をカスタマイズする

FloatingPanelController のDelegateを設定し、 FloatingPanelLayout を継承してカスタマイズしたレイアウトを返すようにします。

// ViewController.Swift

    override func viewDidLoad() {
        super.viewDidLoad()
        
        floatingPanelController = FloatingPanelController()

        // Delegateを設定
        floatingPanelController.delegate = self

        ...
    }

    // FloatingPanelControllerDelegate を実装してカスタマイズしたレイアウトを返す
    extension ViewController: FloatingPanelControllerDelegate {
        func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
            return CustomFloatingPanelLayout()
        }
    }


// CustomFloatingPanelLayout.Swift

class CustomFloatingPanelLayout: FloatingPanelLayout {
    
    // セミモーダルビューの初期位置
    var initialPosition: FloatingPanelPosition {
        return .half
    }
    
    var topInteractionBuffer: CGFloat { return 0.0 }
    var bottomInteractionBuffer: CGFloat { return 0.0 }
    
    // セミモーダルビューの各表示パターンの高さを決定するためのInset
    func insetFor(position: FloatingPanelPosition) -> CGFloat? {
        switch position {
        case .full: return 56.0
        case .half: return 262.0
        case .tip: return 100.0
        }
    }
    
    // セミモーダルビューの背景Viewの透明度
    func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat {
        return 0.0
    }
}

セミモーダルビューを各パターン(full/half/tip)の高さに変更する


floatingPanelController.move(to: .full, animated: true)

セミモーダルビューの高さ変更後に処理を実行する

レイアウトのカスタマイズと同様に FloatingPanelController のDelegateを設定し、 ドラッグ操作完了時のイベントなどを取得して処理を実行することができます。

// ViewController.Swift

    override func viewDidLoad() {
        super.viewDidLoad()
        
        floatingPanelController = FloatingPanelController()

        // Delegateを設定
        floatingPanelController.delegate = self

        ...
    }

    extension ViewController: FloatingPanelControllerDelegate {
    
        func floatingPanelDidEndDragging(_ vc: FloatingPanelController, withVelocity velocity: CGPoint, targetPosition: FloatingPanelPosition) {

            // セミモーダルビューの各表示パターンの高さに応じて処理を実行する
            switch targetPosition {
            case .tip:
                print("tip")
            case .half:
                print("half")
            case .full:
                print("full")
            }
        }
    }

カスタマイズしたセミモーダルビューのサンプル

以下のような機能を追加してみました。

  • tipの位置にドラッグされたときにセミモーダルビューを閉じる
  • キーボードの表示/非表示に合わせてセミモーダルビューの高さを変更する
  • 入力が完了したらセミモーダルをクローズして入力された値を呼び出し元に返す
セミモーダルビュー(カスタムバージョン)
custom_semi_modal.gif

まとめ

「FloatingPanel」 を使うと非常に手軽にセミモーダルビューを追加することができました。

iOSの標準機能として実現されたら、、、と二の足を踏んでいる方も、検証だけでも使ってみる価値があると思います。

また使ってみて気づいたのは、通常の全画面モーダルでは、モーダルで表示している以外の操作をブロックするが、セミモーダルではユーザの操作はブロックされない という違いがあるということです。

実際のプロダクトのUIに取り込む際、全画面モーダルで表示している画面を単純にセミモーダルに置き換えるといろいろ破綻してしまう可能性があります。「FloatingPanel」 を使って実際に試してみることをオススメします!

114
62
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
114
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?