Swift で縦スライダーバーを実装する


Xcode のデフォルトの部品に SliderBar がありますが、
これを縦にしたやつの実装が必要になったので、その実装方法を残しておきます。

完成イメージ

開発環境

Xcode: 10.2.1
iOS: 12.2
Swift: Swift5

実装

実際に実装していきたいと思います

Storyboard

今回は以下のように View を配置しています

- baseView
- スライダーの「つまみ」の可動域となる部分
- sliderView
- スライダーの「つまみ」となる部分

ポイントとしては、baseView の中に sliderView が入っていない点です
baseView の中に sliderView が入ってしまっていると、この後のドラッグ処理実装の際に
座標の計算がおかしくなってしまうので注意してください

ViewController

import UIKit

class ViewController: UIViewController {
    /// スライダーの可動域
    @IBOutlet weak var baseView: UIView!
    /// スライダーのつまみ部分
    @IBOutlet weak var sliderView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupSlider()
    }

    /// スライダーのつまみをドラッグしたのを検知
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first,
            let movedView = touch.view else {
                return
        }
        // ドラッグした view がつまみ(sliderView)ではなければ何もしない
        guard movedView == sliderView else {
            return
        }
        moveSliderView(to: touch.location(in: view))
    }

    /// スライダーの描画設定
    private func setupSlider() {
        let baseViewHalfWidth = baseView.frame.width / 2.0
        let sliderViewHalfWidth = sliderView.frame.width / 2.0
        // baseView の角を丸めたり、つまみを円形にするための処理
        baseView.layer.cornerRadius = baseViewHalfWidth
        sliderView.layer.cornerRadius = sliderViewHalfWidth
    }

    /// つまみを移動させる処理
    private func moveSliderView(to point: CGPoint) {
        // 今回は縦方向のみに動かしたいため、x 座標は sliderView.center.x に固定する
        var movePoint = CGPoint(x: sliderView.center.x,
                                y: point.y)
        // sliderView の center の位置を変更することでドラッグの動きに合わせて移動させる
        sliderView.center = movePoint

    }
}

このようにすると、上下の動きが実装できるかと思います!
ただ、今の状態だと可動域を超えてもドラッグできてしまうので、この部分を修正します

可動域を修正

class ViewController: UIViewController {

    ...

    /// スライダー可動域の Y 座標の上限値
    private var baseViewMaxY: CGFloat! //  <--- 追加
    /// スライダー可動域の Y 座標の下限値
    private var baseViewMinY: CGFloat! //  <--- 追加

    ...

    /// スライダーの描画設定
    private func setupSlider() {
        let baseViewHalfWidth = baseView.frame.width / 2.0
        let sliderViewHalfWidth = sliderView.frame.width / 2.0
        /* 
         上限値を baseView.frame.maxY or minY で取得しており、
         sliderView を動かす際は sliderView.center の値を変更して動かしているため、
         可動域ぴったりに収まるよう調節
         */
        baseViewMaxY = baseView.frame.maxY - baseViewHalfWidth //  <--- 追加
        baseViewMinY = baseView.frame.minY + sliderViewHalfWidth //  <--- 追加
        baseView.layer.cornerRadius = baseViewHalfWidth
        sliderView.layer.cornerRadius = sliderViewHalfWidth
    }

    /// つまみを移動させる処理
    private func moveSliderView(to point: CGPoint) {
        var movePoint = CGPoint(x: sliderView.center.x,
                                y: point.y)
        //  <--- ここから追加 --->
        if movePoint.y > baseViewMaxY {
            // 上限値を超えた場合は上限値以上にならないようにする
            movePoint.y = baseViewMaxY
        }

        if movePoint.y < baseViewMinY {
            // 加減値より小さくなった場合は下限値以下にならないようにする
            movePoint.y = baseViewMinY
        }
        //  <--- ここまで追加 --->
        sliderView.center = movePoint
    }
}

上記のように修正することで、可動域をはみ出ないように制御することが可能になりました!

サンプル

以下に今回実装したコードを上げていますので、良かったら参考にしてください
https://github.com/nwatabou/VerticalSlider