🎚️

[SwiftUI] 対数スケールスライダーを作る

2023/03/05に公開

「速度」「大きさ」など、倍率を設定するUIがしばしば必要になります。

素直に作るとすると、次のようにすれば問題なさそうです。

struct SpeedSettingView: View {
    @State private var speedScale = 1
    
    var body: some View {
        Slider(value: $speedScale, in: 0.25 ... 4)
    }
}

しかし、実際に動かしてみると問題に気づきます。このスライダーでは、「1倍速」と「2倍速」の間はそこそこ長いのに、「1倍速」と「1/2倍速」の間はちょうど半分です。ユーザの感覚としては「2倍にすること」と「1/2倍にすること」は同じ程度の変化量でできるべきことではないでしょうか。

このような場合、1つの解決策は「連続値にしない」ということです。この場合なら、「1/4x」「1/3x」「1/2x」「1x」「2x」「3x」「4x」でPickerを用いることができます。一例ですが、YouTubeの速度調整は非連続な値を選択するUIになっています。

しかし、どうしてもSliderを使いたい場合もしばしばあります。そういう際におすすめなのが、対数スケールスライダーです。

対数スケールスライダー

対数は高校数学で勉強する関数なので、ご存知の方も多いでしょう。良い性質に「積を和に置き換える」という特徴があります。この特徴によって、等比数列の対数を取ることで等差数列にすることができます。

倍率のような量は等比数列的ですが、対数スケールで扱うことで等差数列にすると、直感的になります。

例えば、1/4倍、1/2倍、1倍、2倍、4倍は、対数スケールではそれぞれ-2、-1、0、1、2と等間隔に並びます。このことを利用すれば、「1/2倍にすること」と「2倍にすること」を同じ変化量で実現することができます。

一つの良い例がiOSのカメラアプリです。カメラアプリでは倍率を円形スライダー(?)のUIを用いて微調整することができます。このとき、0.5倍から1倍、1倍から2倍、2倍から4倍の間の間隔はそれぞれ同じになっています。ここでも対数が使われているということです。

実装

SwiftUIのSliderを対数スケール目盛りにする方法は用意されていませんが、Bindingを生成することによって容易に実現できます。

struct SpeedSettingView: View {
    @State private var speedScale = 1
    
    var body: some View {
        Slider(value: Binding(get: {log2(speedScale)}, set: {speedScale = pow(2, $0)}, in: -2 ... 2)
    }
}

これで直感的に操作できるスピード倍率設定のUIを作ることができます。

Discussion