プログラマ英語学習日記

プログラミングと英語学習のまとめなど

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)
    • iPhone/iPadで表示を切り替えてたいときに使う
    • Phone指定でiPhoneでのみ表示、Pad指定でiPadでのみ表示、両方かけば両方で表示(※ただし両方なら明示的に書かなくていい)
<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製以外にここを活用するアプリを見たことがない)。ただしそれが面倒と感じる場合は有効な選択肢になると思います。うまく活用しましょう。