Settings.bundle詳解
iOS開発で時々お世話になるのが Settings.bundle です。
名のごとく設定に関するファイルで、iOS本体の設定アプリに自作アプリの設定を割り込ませることができるというものです。
久しぶりに使ったので今回はこれの解説をします。
Root.plist
まず、Settings.bundle
を作ります。
- Xcode→プロジェクトツリー右クリック→新規ファイル→Settings.bundle
というそのままな項目があるので選択しましょう。名前は変更せずにSettigns.bundle
のままにしてください。
Settings.bundle自体は単なるフォルダで、中にある Root.plist
がメインです(普通自動的に作成されます)。翻訳用Stringファイルは今回は無視。削除します。
ここまできたら一度実行してみましょう。設定アプリ内に自分のアプリが出てきて、その奥にデフォルト作成された設定項目があるはずです。
ただしうまく表示されないことが多い。むしろほとんど反映されません。その場合は一度アプリを削除再インストールすると確実です
設定可能なのは以下の8つ。公式ドキュメント を見てください、で済ませてもいいのですがブログとして面白くないので書きます(笑)
- グループ
- 項目をグループ分けするときに使う。
- 背景がない。文字の表示のみ
- タイトル
- 単なるセクションタイトル。ユーザは変更不可能。
- ただしプログラムからは変更可能。バージョン番号表記などに使える
- テキストフィールド
- トグルスイッチ
- スライダー
- これらはUIKitと見た目/機能が一緒です
- マルチバリュー
- 開発者が指定したいくつかの選択項目から選ばせるもの。単一選択
- Navigationで1階層深い画面に遷移
- ラジオボタン
- 名前はラジオボタンですが、単一項目選択パターン
- マルチバリューと違い、画面遷移がない
- 選択項目が少ないならアリ。多いならマルチバリューにしましょう
- 子階層
- 他の設定用plistに遷移できます
- 項目が複雑になったら
基本的にユーザが変更するような項目は キーとデフォルト値を設定する必要があります。 これらはプログラムから設定値を読み込むときに必要となります。
値の読み込み
読み込みは非常に楽です。
//keyはplistで設定した文字列をいれてください let value = UserDefaults.standard.bool(key: key) //他にもIntやStringなど読み込み可能
PropertyWrapperを使った拡張を用意すると便利です。これについては今回は省略。検索すれば大量にあるので興味があるなら検索してみてください。
注意点として、ユーザが一度も設定を変更していない時、plistで設定したデフォルト値が来るわけではない ということ。設定を変更した経験がない場合は常に nil
(falseだったり0だったりする)です。defaultValueが影響するのは 初期 表示 だけです。そのため、プログラムの初期値とplistの初期値を一致させておく必要があります。
plist詳細
一番の課題がここではないでしょうか?「トグルスイッチを追加できるのは分かったけど、何を/どうplist書けばいいの?」というのはiOS開発あるあるです。ここではコンポーネントごとに詳細を書いていきます。
今回は生XMLで取り扱います。生XMLコードを編集するには、プロジェクトツリーの.plistで 右クリック → OpenAs→SourceCode と選択してください。ただし不正なXMLの場合、そのあとplist専用表示で読み込めなくなるので事前にコミットするなどし、いざというときは戻せるようにしましょう。
以下、"変数名": "型" と書いていきます。Optionalとあるものはあってもなくてもいい、Optionalとないものは必須です。また、同じキーが頻出するので、二度目以降は説明を省略します
グループ
- Type: String
PSGroupSpecifier
にする
- Title: String
- 任意のタイトル。文字列リソースがあるならそちらのキー値を書く
- FooterText: String(Optional)
- タイトル下に表示される小さい文字列
- SupportedUserInterfaceIdioms: Array(Optional)
<dict> <key>Type</key> <string>PSGroupSpecifier</string> <key>Title</key> <string>グループ</string> <key>FooterText</key> <string>小さい文字</string> </dict>
タイトル
- Type: String
PSTitleValueSpecifier
にする
- Title: String
- Key: String
- プログラムからアクセスするための値。UserDefaultsで使う
- DefaultValue: String
- 初期値
- SupportedUserInterfaceIdioms: Array(Optional)
- Values/Titles: Array(Optional)
- ごめんなさい、使い道がわかりませんでした。
<dict> <key>Type</key> <string>PSTitleValueSpecifier</string> <key>Title</key> <string>バージョン</string> <key>Key</key> <string>settings.app.version</string> <key>DefaultValue</key> <string>0.1.0</string> </dict>
トグルスイッチ
- Type: String
PSToggleSwitchSpecifier
にする
- Title: String
- Key: String
- DefaultValue: String
- TrueValue: String(Optional)
- FalseValue: String(Optional)
- TrueValue/FalseValueはセット。後述
<dict> <key>Type</key> <string>PSToggleSwitchSpecifier</string> <key>Title</key> <string>トグルスイッチ</string> <key>Key</key> <string>settings.toggle</string> <key>DefaultValue</key> <true/> </dict>
TrueValue/FalseValueには任意の型、IntegerやStringなどを指定可能です。こうすると、true/falseのときにはboolでなく指定した型の値が返ります。ただし逆にboolでは使えなくなります。使用頻度は極めて低いのではないかと思います。
<dict> <!--上部省略--> <key>TrueValue</key> <string>OK</string> <key>FalseValue</key> <string>NG</string> <!--DefaultValueもTrue/FalseValueにあわせる--> <key>DefaultValue</key> <string>OK</string> </dict>
//初期値nil、ユーザが設定すると OK or NG という文字列 UserDefaults.standard.string(forKey: "settings.toggle") //TrueValue/FalseValueを設定すると常に false になるので注意 UserDefaults.standard.bool(forKey: "settings.toggle")
スライダー
- Type: String
- PSSliderSpecifier にする
- Key: String
- DefaultValue: 数値
- ドキュメント上は
real
ですが、integer
も大丈夫なようです
- ドキュメント上は
- MinimumValue/MaximumValue: 数値
- 最小値/最大値
- MinimumValueImage/MaximumValueImage: String(Optional)
- 最小/最大の横にでる画像リソース名
- 21x21ピクセル
- SupportedUserInterfaceIdioms
<dict> <key>Type</key> <string>PSSliderSpecifier</string> <key>Title</key> <string>スライダー</string> <key>Key</key> <string>settings.slider</string> <key>DefaultValue</key> <real>0.5</real> <key>MinimumValue</key> <integer>0</integer> <key>MaximumValue</key> <integer>1</integer> </dict>
テキストフィールド
- Type: String
PSTextFieldSpecifier
にする
- Title: String
- Key: String
- DefaultValue: String(Optional)
- 面白いことにTextFieldではOptionalです
- IsSecure: Bool(Optional)
- パスワードマスクを使いたい時 true
- KeyboardType: String(Optional)
- キーボード種別
- Alphabet / NumbersAndPunctuation / NumberPad / URL / EmailAddress のいずれか
- デフォルト Alphabet
- AutocapitalizationType: String(Optional)
- 自動大文字化するか
- None / Sentences / Words / AllCharacters のいずれか
- デフォルト None
- AutocorrectionType: String(Optional)
- 自動スペル補正するか
- Default / No / Yes のいずれか
- デフォルト Default
- DefaultはKeyboardType等でYes/Noが変わるのではないかと推測
- SupportedUserInterfaceIdioms
<dict> <key>Type</key> <string>PSTextFieldSpecifier</string> <key>Title</key> <string>テキストフィールド</string> <key>Key</key> <string>settings.textfield</string> <key>DefaultValue</key> <string></string> <key>IsSecure</key> <false/> <key>KeyboardType</key> <string>Alphabet</string> <key>AutocapitalizationType</key> <string>None</string> <key>AutocorrectionType</key> <string>No</string> </dict>
マルチバリュー
- Type: String
PSMultiValueSpecifier
を指定
- Title
- Key
- DefaultValue
- Values: Array
- 設定された値。プログラムから読み込むとき、この値が用いられる
- 型は自由
- Titles: String Array
- 表示文字列
- Valuesと同じ数だけ用意する
- ShortTitle: String Array
- 用意すると、設定トップ画面でこの短い文字列が使われる
- 用意しない場合、Titlesと同じ文字列になる
- SupportedUserInterfaceIdioms
- DisplaySortedByTitle: Bool
- ローカライズ時、Titlesをアルファベット(50音)順ソートしたいときtrue
<dict> <key>Type</key> <string>PSMultiValueSpecifier</string> <key>Title</key> <string>マルチバリュー</string> <key>Key</key> <string>settings.multi</string> <key>DefaultValue</key> <string>default</string> <key>Titles</key> <array> <string>速度を優先する</string> <string>低電力を優先する</string> </array> <key>Values</key> <array> <string>speed</string> <string>battery</string> </array> <key>ShortTitles</key> <array> <string>速度</string> <string>低電力</string> </array> </dict>
ラジオグループ
- Type: String
PSRadioGroupSpecifier
を指定
- Title
- Key
- DefaultValue
- Values: Array
- Titles: String Array
- SupportedUserInterfaceIdioms
- DisplaySortedByTitle: Bool
<dict> <key>Type</key> <string>PSRadioGroupSpecifier</string> <key>Title</key> <string>ラジオボタン</string> <key>Key</key> <string>settings.radio</string> <key>DefaultValue</key> <string>s1</string> <key>Titles</key> <array> <string>選択肢1</string> <string>選択肢2</string> <string>選択肢3</string> </array> <key>Values</key> <array> <string>s1</string> <string>s2</string> <string>s3</string> </array> </dict>
子階層
ちょっと特殊で、先にSettings.bundleの中に、もう一つ設定用.plistを用意する必要があります。 項目を選択すると、その指定した.plistの設定画面に遷移します。
- Type: String
PSChildPaneSpecifier
を指定
- Title
- File
- plistファイル名。拡張子不要なので注意
<!-- Child.plist を作ったとする --> <dict> <key>Type</key> <string>PSChildPaneSpecifier</string> <key>Title</key> <string>別の設定</string> <key>File</key> <string>Child</string> </dict>
まとめ
ざっと設定画面の作り方を紹介しました。公式ドキュメントをまとめただけだったりします(笑)
自分の場合、アプリ内で設定画面を作ってしまうことが多いのであまり使うことはなかったりします(正直、Apple製以外にここを活用するアプリを見たことがない)。ただしそれが面倒と感じる場合は有効な選択肢になると思います。うまく活用しましょう。