[SwiftUI] 対数スケールスライダーを作る
「速度」「大きさ」など、倍率を設定する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