74
41

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.

UISliderのUXをトコトン追究して改善してみる

Last updated at Posted at 2018-08-26

UISliderって・・・?

UISliderはその名の通りスライダーコントロール機能を提供するUIKit標準のUIパーツで、
バー(track)とつまみ(thumb)で構成された値を調節する際などに便利なUIです:sparkles:

slider.png

他のUIViewやUIButtonなどに比べると登場頻度は少ないかもしれませんが、
UISliderの特性を知っておいて損はないと思います:point_up:

本稿では標準のUISliderをサンプルコードを交えながらカスタムし、
UXや使い心地を向上してみたいと思います:sunglasses:

UISliderはthumbのドラッグのみで調節可能

UISliderを用いた値の調節は白い丸のつまみ部分(thumbと呼びます)をドラッグして、
左右に動かすことで直感的な調節操作を実現しています。

UISliderはUIControlクラスを継承しており、
addTarget()することで様々なコントロールイベント(UIControlEvents)をフックすることが可能です。
UISliderで最も使用する頻度が高いUIControlEventsは、.valueChangedでしょう。
下記は、UISliderのvalueの変化をフックするサンプルコードです。
実行してつまみをドラッグすると値が断続的に取れているかと思います。

class ViewController: UIViewController {
    @IBOutlet weak var slider: UISlider! // InterfaceBuilderで定義

    override func viewDidLoad() {
        super.viewDidLoad()
        
        slider.addTarget(self, action: #selector(sliderDidChangeValue(_:)), for: .valueChanged)
    }
    
    @objc func sliderDidChangeValue(_ sender: UISlider) { // @IBActionでも可
        print(sender.value) // 0.0
    }
}

UISliderの個人的にイケてないと感じるポイント

上で説明したように、デフォルトのUISliderは、
thumbをドラッグすることで値を調節することができます。
裏を返せば、thumbをドラッグしなければ値を調節することができません

thumbをつまもうとしてドラッグしたら、空振りして失敗してしまう

皆さんも一度はこのような経験をされているのではないでしょうか?
何個かの改善方法とともにUISliderのユーザビリティを磨きこんでいきます:sparkles:

改善① UISliderのthumb以外をタップやドラッグしても調節可能にする

デフォルトでは、UISliderのタップ領域 = thumbのタップ領域です。
では、どのようなロジックでスライダーの調節開始を判定しているのでしょうか。

UISliderの親クラスであるUIControlのDocumentを見ると、
beginTracking(_:with:)というメソッドが用意されています。

func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool

このメソッドによって、スライダーの調節を開始するべきか判定しています。
おおよそ、デフォルトの実装はtouchされた座標がthumbの座標内にあるかというのを見て、
調節を開始させるかどうか判定しているのだと思われます。

API提供されてる範囲で実装してみるとこんな感じでしょうか。

func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
    // thumbのrectを算出する
    let thumbRect = self.thumbRect(
        forBounds: bounds,
        trackRect: trackRect(forBounds: bounds), // バー(track)のrect
        value: value
    )
    
    // tapした座標
    let tapPoint = touch.location(in: self)
    
    // tapした座標がthumbの矩形内に含まれていれば調節開始する
    return thumbRect.contains(tapPoint)
}

では、このメソッドを無条件にtrueを返却するようにoverrideするとどうなるでしょうか。
どんなtouchでもtrackingを開始するようになるため、
つまみ部分以外の領域を選択しても調節が可能となります。

つまみ(thumb)以外をタップしても調節可能なスライダー
class TappableSlider: UISlider {
    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        return true // どんなtouchでもスライダー調節を行う
    }
}

実際に動かしてみるとこんな感じです!
つまみがない部分をタップしたりドラッグしたりできるようになっているのがわかるかと思います。

TappableSlider.gif

改善② UISliderのタップ領域を広げる

①で説明したthumb以外をタップしても調節可能にする方法は、
UISliderの領域内であればどこでも選択可能となりました。

しかし、UISliderのbounds外をタップしても反応してくれません。
たとえば、こんな感じの目盛り付きのスライダーUIがあるとします。
image.png
黄色い部分はUISliderのbounds領域です。

デフォルトではつまみの選択のみ、スライダーを動かすことができました。
改善①によって、黄色い部分のどこを選択してもスライダーが動くようになりました。
しかし、1-5の目盛りラベルを選択してもスライダーは動きません。
これを反応できるようにタップ領域を拡大してみましょう。

UISliderでタップ領域を決定しているメソッドは、UIControlのpoint(inside:with:)です。
つまり、このメソッドでtrueが返却された後にbeginTracking(_:with:)が呼ばれます

func point(inside point: CGPoint, with event: UIEvent?) -> Bool

こちらのメソッドもDocumentを見る限り、
おおよそ、デフォルトの実装は、pointに入ってくる値が自身のbounds内にあるかどうかでしょう。

つまり、こんな感じだと想定されます。

func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    return bounds.contains(point) // pointがbounds内にあるかどうか
}

ラベル部分は自身のbounds外となるため、
そこを選択してもbeginTrackingが呼ばれませんでした。

では、ラベル部分も選択可能にするために、
UISliderのboundsより20px下まで判定を可能にしてみましょう。

下に20px広くタップが可能なスライダー
class WideTappableSlider: TappableSlider {
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        var wideBounds = bounds
        wideBounds.size.height += 20.0 // boundsを20.0px分下に拡張
        return wideBounds.contains(point) // pointがwideBounds内にあるかどうか
    }
}

これでどうでしょうか。
目盛り部分をタップするとその位置にスライダーを動かすことができるようになりました:tada:
slider2.gif

最後に

最後までお読みいただきありがとうございます:bow:
いかがだったでしょうか?

UISliderのUX改善について2つの方法をまとめてみました。
どちらの方法も親クラスのUIControlのメソッドをoverrideしているので、
UISlider以外のUIKitにも応用が可能だったりします。

他にもUISliderのUX改善法を知っている方がいましたら、
コメントしていただけると幸いです!

こうした細かいチューニングでアプリのUXをどんどん磨いていきましょう。

それでは:raised_hand:

参考リンク

74
41
0

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
74
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?