[iOS][Swift] ボタンにマテリアルデザイン風のフィードバックをつける

2016.06.01

今回はマテリアルデザイン風フィードバックをiOSで作りたいと思います。
マテリアルデザイン風フィードバック = 『画面をタップしたところから円形の影みたいなのを出すモーション』です。
文章で書いても分かり辛いので、まずは下記アニメーションを御覧ください。
sample
Button|Google Material Design Guidelineより
↑ これです。(下ボタンの方)

UIButtonにマテリアルデザイン風のフィードバックをつける

今回はボタンに対して処理を入れたいと思います。
まずはUIButtonのカスタムクラスを作成します。今回はMaterialButton.swiftという名前のファイルを作成しました。

import UIKit
class MaterialButton: UIButton {
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

タップした場所の座標を取得する

まずはタップした場所の座標を取得出来るようにします。
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { ... } でタップの開始を取得します。
touches.first?.locationInView(self)CGPointを返します。

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        super.touchesBegan(touches, withEvent: event)
        if let point = touches.first?.locationInView(self) {
            // ここに処理を書く
        }
    }

フィードバック用のViewをつくる

今度はフィードバックとして表示するViewを生成します。
tapEffectViewというUIViewを用意し、そこに円を書いています。

class MaterialButton: UIButton {

    private let tapEffectView = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))

    override func awakeFromNib() {
        super.awakeFromNib()

        setup()
    }

    private func setup() {
        // ボタン自体を角丸にする
        layer.cornerRadius = 4.0
        layer.masksToBounds = true
        // 円を描画
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = UIColor.grayColor().CGColor
        shapeLayer.path = UIBezierPath(ovalInRect: tapEffectView.bounds).CGPath
        tapEffectView.layer.addSublayer(shapeLayer)
        tapEffectView.hidden = true

        addSubview(tapEffectView)
    }
}

hiddentrueにして見えないようにしてからaddSubviewします。
これでフィードバック用のViewの準備が出来ました。

タップ時のアニメーションをつける

上で作成したフィードバック用のViewをタップ時に表示させます。
タップした場所の座標を取得するで作成したtouchesBeganに処理を追加します。

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        super.touchesBegan(touches, withEvent: event)
        if let point = touches.first?.locationInView(self) {
            tapEffectView.frame.origin = point

            tapEffectView.alpha = 0.3
            tapEffectView.hidden = false
            tapEffectView.transform = CGAffineTransformMakeScale(1.0, 1.0)

            UIView.animateWithDuration(0.5,
                                       animations: {
                                        self.tapEffectView.alpha = 0
                                        self.tapEffectView.transform = CGAffineTransformMakeScale(200.0, 200.0)
            }) { finished in
                self.tapEffectView.hidden = true
                self.tapEffectView.transform = CGAffineTransformMakeScale(1.0, 1.0)
            }
        }
    }

tapEffectView.frame.origin = point でタップした座標にエフェクト用のViewを移動させます。
そしてtapEffectViewに対してScaleのアニメーションを入れました。 (アニメーションは適宜修正してください)

ボタンの適用

作成したMaterialButtonクラスをStoryboard上のボタンに対して設定します。 storyboard_button

結果

実行してみると以下のような感じになります。
結果

まとめ

マテリアルデザインのモーションはiOSでも大変参考になると思います。
Androidの良い所を積極的に取り入れれば、より素敵なアプリになるのではないでしょうか。

Material Motion|Google