Measurement.FormatStyleのあれこれ
Measurement
(数値&単位のデータ)を表示する時、Measurement.FormatStyle
を使ったやり方があります。この記事ではこの Measurement.FormatStyle
について書きます。
おさらい
Measurement
- 値
- 単位
を保持するもの。
作成するときは数値と単位を指定する。
Measurement(value: 100, unit: UnitTemperature.fahrenheit)
華氏の100度
Measurement.formatted()
Measurement
には formatted()
というメソッドがある。String
を返す。
var hundred = Measurement(value: 100, unit: UnitTemperature.fahrenheit)
print(hundred.formatted()) //出力はOSの設定による
出力はOSの設定によるので、華氏に設定してあれば100°Fだし、摂氏に設定してあれば37.777778°Cと表示します。
以下はVenturaの設定画面の例。
Venturaの設定画面
Measurement.FormatStyle
formatted()
の出力結果がOSの設定によるのは使いにくいでかんわという場合は、形式を指定する次のものがあります。
func formatted<S>(_ style: S) -> S.FormatOutput where S : FormatStyle, S.FormatInput == Measurement<UnitType>
この引数に与えるものが FormatStyle
で、本日の主役になります。
プロトコルの FormatStyle
があり、それを実装したstructの Measurement.FormatStyle
があります。同じ名前なのでややこしいです。FormatStyle
はSwiftのみで、iOS15から。
formatted(引)
の話に戻ります。引数の FormatStyle
のインスタンスを作るときは使用頻度の高い設定を抜粋した次のメソッドが便利です。
static func measurement<UnitType>(
width: Measurement<UnitType>.FormatStyle.UnitWidth,
usage: MeasurementFormatUnitUsage<UnitType> = .general,
numberFormatStyle: FloatingPointFormatStyle<Double>? = nil
) -> Measurement<UnitType>.FormatStyle where UnitType : Dimension
定義の見た目はゴツいですが、使い方は次のようになります。
var hundred = Measurement(value: 100, unit: UnitLength.meters)
hundred.formatted(.measurement(
width: .narrow,
usage: .general))
引数で設定した2つの意味は
- width 表示の文字の長さ metersやmなどを切り替えます
- usage 何の単位を使うかの判断方式
です。この他に数値の形式を指定する引数もありますが、世の中に情報が多いと思うのでこの記事では触れません。
サンプルを見てください。まずはwidthを切り替えた様子です。
var hundred1 = Measurement(
value: 100, unit: UnitLength.meters)
hundred1.formatted(.measurement(
width: .narrow,
usage: .general)) //100m
hundred1.formatted(.measurement(
width: .abbreviated,
usage: .general)) //100 m
hundred1.formatted(.measurement(
width: .wide,
usage: .general)) //100 meters
widthを切り替えると単位の文字列の長さが変わります。abbreviatedとは省略されたという意味です。abbreviatedよりもnarrowの方が短い。言語の仕様で3パターン用意しておいて、そこに何を当てはめるかは各単位ごとに決めるという感じのようです。
次にusageを切り替えます。
var hundred2 = Measurement(
value: 100, unit: UnitLength.millimeters)
hundred2.formatted(.measurement(
width: .narrow,
usage: .asProvided)) //100mm
hundred2.formatted(.measurement(
width: .narrow,
usage: .general)) //10cm
usageを .asProvided
にすると設定された通りの単位(ミリメートル)を選択しました。
.general
はより自然にcmを選択しました。
usageの違いを今度は温度で確認します。
var hundred3 = Measurement(
value: 100, unit: UnitTemperature.fahrenheit)
hundred3.formatted(.measurement(
width: .abbreviated,
usage: .asProvided)) //100℉
hundred3.formatted(.measurement(
width: .abbreviated,
usage: .general)) //37.777778℃
華氏でデータを作成し、.asProvided
で表示すると華氏になります。.general
にすると、実行環境の設定が摂氏なので摂氏になりました。
usageは単位ごとにいろいろあります。長さだと、人、道、レンズの焦点などがあります。
ローカライズ
locale()
というメソッドがあります。このように使います。
var hundred4 = Measurement(value: 100, unit: UnitTemperature.fahrenheit)
hundred4.formatted(.measurement(width: .wide, usage: .asProvided)
.locale(Locale(identifier: "ja_JP")))
measurement
の生成物に対して locale
を投げる形です。
ローカライズには
- 地域 (単位もこちらに含まれる)
- 言語
の2要素があります。locale
の挙動を見る時はこれを意識する必要があります。
usage: .asProvided のとき
言語の方だけに効果があるようです。つまり単位は .measurement()
で決定し、その表示言語を .locale()
で切り替えました。
var hundred4 = Measurement(value: 100, unit: UnitTemperature.fahrenheit)
hundred4.formatted(.measurement(width: .wide, usage: .asProvided)
.locale(Locale(identifier: "ja_JP"))) //華氏100度
hundred4.formatted(.measurement(width: .wide, usage: .asProvided)
.locale(Locale(identifier: "en_US"))) //100 degrees Fahrenheit
var hundred5 = Measurement(value: 100, unit: UnitTemperature.celsius)
hundred5.formatted(.measurement(width: .wide, usage: .asProvided)
.locale(Locale(identifier: "ja_JP"))) //摂氏100度
hundred5.formatted(.measurement(width: .wide, usage: .asProvided)
.locale(Locale(identifier: "en_US"))) //100 degrees Celsius
usage: .general のとき
地域と言語両方に適用されました。
var hundred4 = Measurement(value: 100, unit: UnitTemperature.fahrenheit)
hundred4.formatted(.measurement(width: .wide, usage: .general)
.locale(Locale(identifier: "ja_JP"))) //摂氏38度
hundred4.formatted(.measurement(width: .wide, usage: .general)
.locale(Locale(identifier: "en_US"))) //100 degrees Fahrenheit
var hundred5 = Measurement(value: 100, unit: UnitTemperature.celsius)
hundred5.formatted(.measurement(width: .wide, usage: .general)
.locale(Locale(identifier: "ja_JP"))) //摂氏100度
hundred5.formatted(.measurement(width: .wide, usage: .general)
.locale(Locale(identifier: "en_US"))) //212 degrees Fahrenheit
温度だけにアクセスを許されたものがある
- 100 degrees
- 100 degrees Fahrenheit
のように摂氏/華氏の情報を表示するかどうかを決める設定があります。要はFahrenheitという言葉を表示しないように出来ます。
hidesScaleName
trueにすると摂氏/華氏の情報を表示しない。
ただし、usageが asProvided
のときのみ有効になります。つまり、環境により単位が変更される可能性があるときは、この摂氏/華氏の情報を表示しない機能は無効です。
参考
↓プロトコルのFormatStyle
↓structのFormatStyle
読む必要がないあとがき
iOSアプリ開発の世界では昔からデータと出力形式は分けるものだった。有名なところで、
- Date
- DateFormatter
というのがある。Dateは数直線上のある一点を表すもの。DateFormatterは出力形式。このように分割してDateをシンプルにするやり方だった。
しかし最近は、データに出力形式を含むやり方になってきた。このやり方は データ.出力せよ()
と書けるので、正直こっちの方がコードは書きやすい。
Discussion