苹果官方文档

业界举例:

① 招行的后台运行时长选择列表

② 概念画板的Licenses展示

③ 搜狗输入法

④ outlook默认邮箱

⑤ 微博


Settings Bundle 实际上是Root.plist配置文件与NSUserDefault相关联的

使用之前需要先创建这样一个文件,target里找到settings bundle直接创建就行

然后需要在代码里添加注册代码

//MARK: - 注册setting bundle
- (void)registerDefaultsFromSettingsBundle{
    NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
    if(!settingsBundle) {
        NSLog(@"Could not find Settings.bundle");
        return;
    }
    NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
    NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
    NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
    for(NSDictionary *prefSpecification in preferences) {
        NSString *key = [prefSpecification objectForKey:@"Key"];
         if(key) {
            [defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
        }
    }
    [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
}

-application:didFinishLaunchingWithOptions:方法里调用即可实现配置的同步 (也可在进入前台时调用)

Group

Key Value type Value
Type (required) String 类似, PSGroupSpecifier 这个值是固定写法
Title (localizable) String 标题,类似tableview的组头
FooterText (localizable) String 类似tableview的footer
SupportedUserInterfaceIdioms Array 标识平台,支持多个平台差异化,可以创建多个类似文件,该字段作为标识

示例:

        <dict>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
            <key>Title</key>
            <string>header标题文字</string>
            <key>FooterText</key>
            <string>footer文字</string>
        </dict>

Title和FooterText可以不填内容可用于分隔空间

<dict>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
            <key>Title</key>
            <string></string>
</dict>

Multi Value Element

列表选择,只有单选模式,分为两个层级,外层显示默认选项或者已选选项,内层显示列表页

Key Value type Description
Type (required) String 类似 PSMultiValueSpecifier 固定写法.
Title (required, localizable) String 标题
Key (required) String 关联userdefault偏好设置保存的值,这就是key
DefaultValue (required) Any 默认值(必填)
Values (required) Array 列表值数组,选取的标题对应的值
Titles (required, localizable) Array 列表标题数组
ShortTitles (localizable) Array 简短标题用于选中时同时显示标题和值(没有实际操作过)
SupportedUserInterfaceIdioms Array 平台标识
DisplaySortedByTitle Boolean 标题排序显示

示例:

<dict>
            <key>Type</key>
            <string>PSMultiValueSpecifier</string>
            <key>DisplaySortedByTitle</key>
            <true/>
            <key>Title</key>
            <string>环境切换</string>
            <key>Key</key>
            <string>default-env</string>
            <key>DefaultValue</key>
            <string>stg1</string>
            <key>Values</key>
            <array>
                <string>prd</string>
                <string>stg1</string>
                <string>stg2</string>
                <string>stg3</string>
                <string>stg4</string>
            </array>
            <key>Titles</key>
            <array>
                <string>prd</string>
                <string>stg1</string>
                <string>stg2</string>
                <string>stg3</string>
                <string>stg4</string>
            </array>
        </dict>

RadioGroup

相对于Multi Value Element只是少了一层级的包装,扁平化的显示这个列表而已

Key Value type Value
Type (required) String 类型PSRadioGroupSpecifier固定写法
Title (localizable) String 标题.
FooterText (localizable) String footer组尾
Key (required) String 偏好设置关联的key.
DefaultValue (required) Any 默认值
Values (required) Array 值数组
Titles (required, localizable) Array 标题数组
SupportedUserInterfaceIdioms Array 平台标识
DisplaySortedByTitle Boolean 按标题排序显示

示例:

<dict>
            <key>Type</key>
            <string>PSRadioGroupSpecifier</string>
            <key>DisplaySortedByTitle</key>
            <true/>
            <key>Title</key>
            <string>RadioGroup列表展示</string>
            <key>FooterText</key>
            <string>这是底部说明文字?</string>
            <key>Key</key>
            <string>show-radio-group</string>
            <key>DefaultValue</key>
            <string>stg1</string>
            <key>Values</key>
            <array>
                <string>prd</string>
                <string>stg1</string>
                <string>stg2</string>
                <string>stg3</string>
                <string>stg4</string>
            </array>
            <key>Titles</key>
            <array>
                <string>prd</string>
                <string>stg1</string>
                <string>stg2</string>
                <string>stg3</string>
                <string>stg4</string>
            </array>
        </dict>

Toggle Switch Element

开关

Key Value type Value
Type (required) String 开关类型PSToggleSwitchSpecifier固定写法
Title (required, localizable) String 标题
Key (required) String 偏好设置关联的key.
DefaultValue (required) Any 默认值.
TrueValue Any True关联的值,可以是数字,日期,字符串,二进制, 如果不设置则默认为布尔值类型
FalseValue Any False关联的值,可以是数字,日期,字符串,二进制, 如果不设置则默认为布尔值类型
SupportedUserInterfaceIdioms Array 平台标识

示例:

<dict>
            <key>Type</key>
            <string>PSToggleSwitchSpecifier</string>
            <key>Key</key>
            <string>demo-click-effect</string>
            <key>Title</key>
            <string>点击效果</string>
            <key>DefaultValue</key>
            <string>NO</string>
        </dict>

Slider Element

进度滑块

Key Value type Value
Type String 类型PSSliderSpecifier固定写法
Key (required) String 关联的key
DefaultValue (required) Real 默认值 (浮点或者整数)
MinimumValue (required) Real 最小值
MaximumValue (required) Real 最大值
MinimumValueImage String 最小值图片, 21x 21 px
MaximumValueImage String 最小值图片, 21×21 px
SupportedUserInterfaceIdioms Array 平台标识

示例:

<dict>
            <key>Type</key>
            <string>PSSliderSpecifier</string>
            <key>MinimumValueImage</key>
            <!-- 最小值图片名 -->
            <string>left</string>
            <key>MaximumValueImage</key>
            <!-- 最大值图片名 -->
            <string>right</string> 
            <key>Key</key>
            <string>progress-bar</string>
            <key>DefaultValue</key>
            <real>0.5</real>
            <key>MinimumValue</key>
            <integer>0</integer>
            <key>MaximumValue</key>
            <integer>1</integer>
        </dict>

Text Field Element

Key Value type Value
Type (required) String 类型PSTextFieldSpecifier`固定写法
Title (localizable) String 标题
Key (required) String 偏好设置关联的key.
DefaultValue String 默认值
IsSecure Boolean 是否为安全输入
KeyboardType String 键盘类型
AutocapitalizationType String 自动首字母大小写模式
AutocorrectionType String 自动纠正类型
SupportedUserInterfaceIdioms Array 支持平台

不知道苹果故意为之还是bug,输入框缺少placeholder字段,官方文档也未提及

示例:

<dict>
            <key>Type</key>
            <string>PSTextFieldSpecifier</string>
            <key>Title</key>
            <string>密码</string>
            <key>DefaultValue</key>
            <string>123456</string>
            <key>IsSecure</key>
            <true/>
            <key>KeyboardType</key>
            <string>NumbersAndPunctuation</string>
            <key>AutocapitalizationType</key>
            <string>None</string>
            <key>AutocorrectionType</key>
            <string>No</string>
            <key>Key</key>
            <string>pwd-demo-xxx</string>
        </dict>

Title Element

标题节点,无事件交互纯信息展示的一个cell

Key Value type Description
Type (required) String 类型 PSTitleValueSpecifier固定写法
Title (localizable) String 标题
Key (required) String 关联的key
DefaultValue (required) String 默认值
Values Array 本地化展示的值
Titles (localizable) Array 本地化展示的标题
SupportedUserInterfaceIdioms Array 支持平台标识

示例:

<dict>
            <key>Type</key>
            <string>PSTitleValueSpecifier</string>
            <key>Title</key>
            <string>君不见</string>
            <key>DefaultValue</key>
            <string>黄河之水天上来</string>
            <key>Key</key>
            <string>cell-display</string>
        </dict>

Child Pane Element

子面板节点
需基于父节点或者根节点来实现,写法与根节点没差别,只是需要在根节点配置入口,指定子节点plist文件的名称

Key Type Value
Type (required) String 类型PSChildPaneSpecifier 固定写法
Title (required, localizable) String 标题
File (required) String 创建的文件,放到bundle里,填写字段时无需后缀名
SupportedUserInterfaceIdioms Array 平台标识

示例:
创建了一个child.plist的文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>StringsTable</key>
    <string>child</string>
    <key>PreferenceSpecifiers</key>
    <array>
        <dict>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
            <key>Title</key>
            <string>这是子面板组头标题</string>
            <key>FooterText</key>
            <string>子面板菜单尾部说明文字</string>
        </dict>
        <dict>
            <key>Type</key>
            <string>PSMultiValueSpecifier</string>
            <key>DisplaySortedByTitle</key>
            <true/>
            <key>Title</key>
            <string>列表展示</string>
            <key>Key</key>
            <string>sssss</string>
            <key>DefaultValue</key>
            <string>stg1</string>
            <key>Values</key>
            <array>
                <string>prd</string>
                <string>stg1</string>
                <string>stg2</string>
                <string>stg3</string>
                <string>stg4</string>
            </array>
            <key>Titles</key>
            <array>
                <string>prd</string>
                <string>stg1</string>
                <string>stg2</string>
                <string>stg3</string>
                <string>stg4</string>
            </array>
        </dict>
        <dict>
            <key>Type</key>
            <string>PSChildPaneSpecifier</string>
            <key>Title</key>
            <string>详情信息展示</string>
            <key>File</key>
            <string>detail</string>
        </dict>
    </array>
</dict>
</plist>

child.plist又关联了一个detail.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>StringsTable</key>
    <string>detail</string>
    <key>PreferenceSpecifiers</key>
    <array>
        <dict>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
            <key>Title</key>
            <string></string>
            <key>FooterText</key>
            <string>The name of the schema file to load. (This file must be a property list file.) The string you specify for this key should not contain path information or the .plist filename extension of your schema file. The Settings app looks in the top-level of your settings bundle for a .plist file with the specified name. For example, if you had a MyPrefs.plist file, you would assign the value MyPrefs to this key. This key is required.</string>
        </dict>
    </array>
</dict>
</plist>

概念画板.app 就是这种实现,把开源的一些三方库的LICENSES全部展示在settings bundle里,就是用的子面板+group

Root.plist里关联只需如下即可:

<dict>
            <key>Type</key>
            <string>PSChildPaneSpecifier</string>
            <key>Title</key>
            <string>更多信息</string>
            <key>File</key>
            <string>child</string>
</dict>

把以上plist转成json来看就比较直观了(plist和JSON互转工具,关注公众号”飙码人生Coding” 回复关键字 20220321 领取)

Root.json

{
    "StringsTable": "Root",
    "PreferenceSpecifiers": [
        {
            "Type": "PSTitleValueSpecifier",
            "Title": "君不见",
            "Key": "cell-display",
            "DefaultValue": "黄河之水天上来"
        },
        {
            "DefaultValue": "123456",
            "KeyboardType": "NumbersAndPunctuation",
            "AutocorrectionType": "No",
            "Key": "pwd-demo-xxx",
            "Type": "PSTextFieldSpecifier",
            "Title": "密码",
            "IsSecure": true,
            "AutocapitalizationType": "None"
        },
        {
            "MaximumValue": 1,
            "DefaultValue": 0.5,
            "Key": "progress-bar",
            "MaximumValueImage": "right",
            "MinimumValue": 0,
            "Type": "PSSliderSpecifier",
            "MinimumValueImage": "left"
        },
        {
            "Title": "iOS SDK Demo",
            "Type": "PSGroupSpecifier",
            "FooterText": "与demo里的配置一一对应且同步,这里仅是提供一个可操作的面板菜单而已"
        },
        {
            "Titles": [
                "prd",
                "stg1",
                "stg2",
                "stg3",
                "stg4"
            ],
            "DefaultValue": "stg1",
            "Values": [
                "prd",
                "stg1",
                "stg2",
                "stg3",
                "stg4"
            ],
            "Key": "default-env",
            "Type": "PSMultiValueSpecifier",
            "Title": "环境切换",
            "DisplaySortedByTitle": true
        },
        {
            "Type": "PSToggleSwitchSpecifier",
            "Title": "埋点开关",
            "Key": "track-enable",
            "DefaultValue": "1.0.0"
        },
        {
            "Type": "PSToggleSwitchSpecifier",
            "Title": "点击效果",
            "Key": "demo-click-effect",
            "DefaultValue": "NO"
        },
        {
            "Type": "PSToggleSwitchSpecifier",
            "Title": "遮罩蒙层",
            "Key": "shield-effect",
            "DefaultValue": "NO"
        },
        {
            "Type": "PSToggleSwitchSpecifier",
            "Title": "异常捕获(保活一次)",
            "Key": "sw-exception-catch",
            "DefaultValue": "NO"
        },
        {
            "Type": "PSToggleSwitchSpecifier",
            "Title": "录屏结束展示性能图表?",
            "Key": "sw-echarts-display",
            "DefaultValue": "NO"
        },
        {
            "Title": "",
            "Type": "PSGroupSpecifier"
        },
        {
            "Type": "PSTitleValueSpecifier",
            "Title": "SDK版本",
            "Key": "SDKVersion",
            "DefaultValue": "1.0.0"
        },
        {
            "Title": "<!-- 这是一条分割线  -->",
            "Type": "PSGroupSpecifier"
        },
        {
            "Title": "以下是DC配置的环境json文本",
            "Type": "PSGroupSpecifier",
            "FooterText": "敏感信息已和谐"
        },
        {
            "Title": "",
            "Type": "PSGroupSpecifier"
        },
        {
            "Title": "更多信息",
            "Type": "PSChildPaneSpecifier",
            "File": "child"
        }
    ]
}

child.json

{
    "StringsTable": "child",
    "PreferenceSpecifiers": [
        {
            "Title": "这是子面板组头标题",
            "Type": "PSGroupSpecifier",
            "FooterText": "子面板菜单尾部说明文字"
        },
        {
            "Titles": [
                "prd",
                "stg1",
                "stg2",
                "stg3",
                "stg4"
            ],
            "DefaultValue": "stg1",
            "Values": [
                "prd",
                "stg1",
                "stg2",
                "stg3",
                "stg4"
            ],
            "Key": "sssss",
            "Type": "PSMultiValueSpecifier",
            "Title": "列表展示",
            "DisplaySortedByTitle": true
        },
        {
            "Title": "详情信息展示",
            "Type": "PSChildPaneSpecifier",
            "File": "detail"
        },
        {
            "Values": [
                "prd",
                "stg1",
                "stg2",
                "stg3",
                "stg4"
            ],
            "DefaultValue": "stg1",
            "Titles": [
                "prd",
                "stg1",
                "stg2",
                "stg3",
                "stg4"
            ],
            "Key": "show-radio-group",
            "Type": "PSRadioGroupSpecifier",
            "Title": "RadioGroup列表展示",
            "FooterText": "这是底部说明文字?",
            "DisplaySortedByTitle": true
        }
    ]
}

detail.json

{
    "StringsTable": "detail",
    "PreferenceSpecifiers": [
        {
            "Title": "",
            "Type": "PSGroupSpecifier",
            "FooterText": "The name of the schema file to load. (This file must be a property list file.) The string you specify for this key should not contain path information or the .plist filename extension of your schema file. The Settings app looks in the top-level of your settings bundle for a .plist file with the specified name. For example, if you had a MyPrefs.plist file, you would assign the value MyPrefs to this key. This key is required."
        }
    ]
}

展示App代码分支节点信息

#!/bin/bash 

#最后一次提交的SHA
last_git_sha=$(git log -1 --format="%h")
#当前的分支
current_git_branch=$(git symbolic-ref --short -q HEAD)

#最后一次提交的作者
git_last_commiter=$(git log -1 --pretty=format:'%an')

#最后一次提交的时间
git_last_commit_date=$(git log -1 --format='%cd')

#编译时间
build_time=$(date)

#message="git_sha: ${last_git_sha}\nbranch: ${current_git_branch}\ncommiter: ${git_last_commiter}\ncommit_date: ${git_last_commit_date}\nbuildTime: ${build_time}"


item=$(cat <<EOF 
{
  "StringsTable": "detail",
  "PreferenceSpecifiers": [
    {
      "Title": "git sha",
      "Type": "PSTitleValueSpecifier",
      "DefaultValue" : "${last_git_sha}",
      "Key": "sha"
    },
     {
      "Title": "当前分支",
      "Type": "PSTitleValueSpecifier",
      "DefaultValue" : "${current_git_branch}",
      "Key": "branch"
    },
     {
      "Title": "提交者",
      "Type": "PSTitleValueSpecifier",
      "DefaultValue" : "${git_last_commiter}",
      "Key": "commiter"
    },
     {
      "Title": "提交时间",
      "Type": "PSTitleValueSpecifier",
      "DefaultValue" : "${git_last_commit_date}",
      "Key": "commit_date"
    },
     {
      "Title": "上一次编译时间(倒数第二次)",
      "Type": "PSTitleValueSpecifier",
      "DefaultValue" : "${build_time}",
      "Key": "build_time"
    }
  ]}
EOF
)
echo $item > ./info.json
plutil -convert xml1 ./info.json -o ./gitInfo.plist
rm -rf ./info.json
mv  ./gitInfo.plist  $SRCROOT/*/Settings.bundle/ 
rm -rf ./gitInfo.plist

利用group的footerText展示文字(未完善)

#!/bin/bash 
# 获取setting bundle 描述 
# 通过group footerText进行展示文本的内容
# 封装成自动操作 更方便把需要展示的文本文件转成plist格式 转成功拖入setting bundle即可使用

# 多行文本读取压缩成一行 json换行不加转义的话会报错
# text=$(cat ${1} | perl -ne 'chomp;print')  #不得行
text=$(cat ${1} | tr "\n" "  " | tr "\"" "'") #勉强能用 效果并不好 要格式好看还是得手动加

text="\"${text}\""
item=$(cat <<EOF 
{
  "StringsTable": "detail",
  "PreferenceSpecifiers": [
     {
      "Title": "",
      "Type": "PSGroupSpecifier",
      "FooterText": ${text}
    }
  ]}
EOF
)

echo "${item}" > "${1%.*}".json
plutil -convert xml1 "${1%.*}".json -o "${1%.*}".plist
rm -rf "${1%.*}".json