InAppSettingsKit
InAppSettingsKit (IASK) 是一个开源框架,可以轻松地将内联设置添加到您的 iOS 或 Catalyst 应用中。iOS 应用通常使用 Settings.bundle
资源来在设置应用中添加特定于应用的设置。InAppSettingsKit 利用相同的捆绑包,允许您在应用内展示相同的设置屏幕。因此,用户可以选择在哪里更改设置。
IASK 不仅复制了系统设置的特性集,还支持大量其他元素和配置选项。
从 IASK 2.x 升级? 请阅读 发布说明。
它是如何工作的?
为了支持传统的 Settings.app 标签页,应用程序必须包含一个至少包含 Root.plist
的 Settings.bundle
,用于指定设置 UI 元素与 NSUserDefaults
键的连接。InAppSettingsKit基本上使用相同的 Settings.bundle 来完成其工作。这意味着当您想要包含一个新的设置参数时,无需额外的工作。只需将其添加到 Settings.bundle,它就会同时在应用程序和 Settings.app 中显示。支持所有类型的设置,如文本字段、滑块、切换元素、子视图等。
如何包含它?
源代码可在 github 上找到。有多种安装方式
使用 SPM
将其添加到您的 Package.swift 中
.package(name: "InAppSettingsKit", url: "https://github.com/futuretap/InAppSettingsKit.git", .branch("master"))
或者,前往 Xcodes 的 文件->Swift 包->添加包依赖...
菜单项,并添加 https://github.com/futuretap/InAppSettingsKit.git
。
使用 CocoaPods
将其添加到您的 Podfile 中
pod 'InAppSettingsKit'
使用 Carthage
将其添加到您的 Cartfile 中
github "futuretap/InAppSettingsKit" "master"
App 集成
为了开始使用 IASK,将 Settings.bundle
添加到您的项目中(文件
-> 添加文件
-> 设置捆绑包
),并使用您的设置编辑 Root.plist
(有关 Apple 关于 方案文件根内容 的说明)。继续阅读以了解更多高级用法。
要显示 InAppSettingsKit,实例化 IASKAppSettingsViewController
并将其推送到导航堆栈或嵌入导航控制器的根视图控制器。
使用 Swift 代码在代码中
let appSettingsViewController = IASKAppSettingsViewController()
navigationController.pushViewController(appSettingsViewController, animated: true)
使用 Objective-C 代码在代码中
IASKAppSettingsViewController *appSettingsViewController = [[IASKAppSettingsViewController alloc] init];
[self.navigationController pushViewController:appSettingsViewController animated:YES];
通过Storyboard
- 将嵌入到导航控制器中的 Table View Controller 拖放到您的应用程序中,并通过Storyboard将其与您的应用程序UI连接
- 将 Table View Controller 类设置为
IASKAppSettingsViewController
- 将 Table View 设置为 "分组" 样式。
- 如果您是以模态方式显示导航控制器
- 在 Table View Controller 中,在“应用程序设置视图控制器”下将“显示完成按钮”设置为“开”
- 设置符合
IASKAppSettingsViewControllerDelegate
的代理。 - 实现代理方法
-settingsViewControllerDidEnd:
并关闭视图控制器。
示例应用程序显示了如何连接所有内容。
其他更改
为了自定义行为,实现 IASKSettingsDelegate
并将 IASKAppSettingsViewController
的 delegate
属性设置。对于更高级的自定义需求,支持对 IASKAppSettingsViewController 的子类化。
根据您的项目,可能需要在应用程序的启动代码中做出一些更改。您的应用程序必须在运行时能够根据用户的更改重新配置本身。这可以通过在 -applicationDidFinishLaunching
中调用的 -reconfigure
方法以及 IASKAppSettingsViewController
的代理方法 -settingsViewControllerDidEnd:
来完成。
附加功能
InAppSettingsKit 的目的是创建对 Settings.app 100% 的模仿(见Apple 设置应用架构参考)。在此基础上,我们还添加了大量附加功能,使 IASK 更加灵活和动态。
自定义 application 文件
设置 application 文件可能依赖于设备:iPad 上使用 Root~ipad.plist
,iPhone 上使用 Root~iphone.plist
。如果不存在,则使用 Root.plist
。
InAppSettingsKit 允许您使用 .inApp.plist
而不是 .plist
来覆盖这些标准文件。或者,您可以创建一个名为 InAppSettings.bundle
的单独的包,而不是通常的 Settings.bundle
。后者适用于您想抑制 Settings.app 中的设置。
这是完全的搜索顺序
- InAppSettings.bundle/FILE~DEVICE.inApp.plist
- InAppSettings.bundle/FILE.inApp.plist
- InAppSettings.bundle/FILE~DEVICE.plist
- InAppSettings.bundle/FILE.plist
- Settings.bundle/FILE~DEVICE.inApp.plist
- Settings.bundle/FILE.inApp.plist
- Settings.bundle/FILE~DEVICE.plist
- Settings.bundle/FILE.plist
隐私链接
如果应用在Info.plist
中包含了对相机或位置访问等隐私功能的使用密钥,IASK将在设置页面的顶部显示一个“隐私”单元格。此单元格将打开系统设置应用并显示应用的设置面板,用户可以在此指定应用的隐私设置。
如果不想显示隐私单元格,请将属性neverShowPrivacySettings
设置为YES
。
示例应用定义了NSMicrophoneUsageDescription
以使单元格显示出来。请注意,设置页面还没有显示任何隐私设置,因为应用实际上并没有访问麦克风。隐私设置只在使用受保护的隐私API之后在设置应用中显示。
打开URL
InAppSettingsKit添加了一个新元素IASKOpenURLSpecifier
,允许使用外部应用(例如Safari或Mail)打开指定的URL。启动的URL在File
参数中指定。有关详细信息,请参阅示例Root.inApp.plist
。
邮件编辑器
自定义的IASKMailComposeSpecifier
元素允许通过打开邮件编辑视图在应用内发送邮件。您可以使用设置plist设置以下(可选)参数:IASKMailComposeToRecipents
、IASKMailComposeCcRecipents
、IASKMailComposeBccRecipents
、IASKMailComposeSubject
、IASKMailComposeBody
、IASKMailComposeBodyIsHTML
。
- (BOOL)settingsViewController:(id<IASKViewController>)settingsViewController shouldPresentMailComposeViewController:(MFMailComposeViewController*)mailComposeViewController forSpecifier:(IASKSpecifier*)specifier;
在您的代理中实现以下代码以自定义邮件(例如,使用动态内容预填充正文、添加附件)修改编辑视图的外观,甚至阻塞标准呈现。如果设备上未配置电子邮件,将显示一个警告。
IASKSpecifier
是定义单个设置单元格的内部模型对象。重要的IASKSpecifier属性key
:对应于设置plist中的Key
title
:设置键的本地化标题type
:对应于设置plist中的Type
defaultValue
:对应于设置plist中的DefaultValue
InAppSettingsKit 添加了一个 IASKButtonSpecifier
元素,允许调用自定义操作。只需添加以下代理方法:
- (void)settingsViewController:(IASKAppSettingsViewController*)sender buttonTappedForSpecifier:(IASKSpecifier*)specifier;
发送者始终是 IASKAppSettingsViewController
的一个实例,它是 UIViewController
的子类。因此,您可以访问其视图属性(可能用于显示操作表)或推送另一个视图控制器。另一个便捷的特性是,IASK按钮的标题可以由来自 NSUserDefaults
(或任何其他设置存储 - see below)的可本地化值覆盖。这对于切换按钮(例如,登录/注销)很有用。请参阅示例应用程序以获取详细信息。
默认情况下,按钮居中对齐,除非指定了图像(默认:左对齐)。默认对齐方式可以被覆盖。
多行文本视图
类似于标准文本字段,IASKTextViewSpecifier
显示一个全宽多行文本视图,根据输入的文本进行大小调整。它还支持 KeyboardType
、AutocapitalizationType
和 AutocorrectionType
。
日期选择器
IASKDatePickerSpecifier
显示一个 UIDatePicker
来设置日期和时间。它支持以下选项:
DatePickerMode
:以下是《Date》、《Time》或《DateAndTime》之一(请参阅 UIDatePickerMode)。默认是《DateAndTime》。DatePickerStyle
:以下之一为《Compact》、《Wheels》或《Inline》(请参阅 UIDatePickerStyle)。默认是《Wheels》。此功能需要 iOS 14 或更高版本。如果不支持此操作系统,IASK 将回退到《Wheels》。MinuteInterval
:日期选择器显示分钟的间隔。默认:1。
有3个可选的委托方法可以自定义日期和时间的存储和显示方式
- (NSDate*)settingsViewController:(IASKAppSettingsViewController*)sender dateForSpecifier:(IASKSpecifier*)specifier;
实现此方法,如果日期/时间的存储格式不是NSDate
对象。当用户通过选择日期/时间选择器上方的标题单元格开始编辑日期/时间时调用。
- (NSString*)settingsViewController:(IASKAppSettingsViewController*)sender datePickerTitleForSpecifier:(IASKSpecifier*)specifier;
实现此方法来自定义日期/时间选择器上方标题单元格中显示的值。
- (void)settingsViewController:(IASKAppSettingsViewController*)sender setDate:(NSDate*)date forSpecifier:(IASKSpecifier*)specifier;
实现此方法,如果日期/时间的存储格式不是NSDate
对象。当用户使用选择器更改日期/时间值时调用。
列表组
列表组(IASKListGroupSpecifier
)是仅适用于IASK的特性,允许您管理变量数量的事项,包括添加和删除。标签、账户、名称数组是典型应用场景。列表组包含多个ItemSpecifier
事项。这些事项的数量由您在NSUserDefaults(或您的自定义设置存储)中的实际内容确定。换句话说,ItemSpecifier
定义了单元格类型,而单元格数量和内容则来自NSUserDefaults或您的存储。如果将Deletable
参数设置为YES,则可以通过滑动删除单元格。
可选的,列表组还有一个AddSpecifier
来控制列表组部分的最后一个项目。它用于添加事项,可以是文本框、切换按钮、滑块或子面板。前三个在编辑完成后创建新项目,而子面板则显示模态子视图控制器来配置复杂项,作为字典保存。这些子面板与正常子面板非常类似,只有一些不同:它们不是通过推送方式显示,而是通过模态显示,在导航栏中有一个取消和完成按钮。通过点击完成按钮创建新项目。
您可能希望指定一些需要在启用完成按钮之前满足的验证规则。这可以通过代理方法实现:
- (BOOL)settingsViewController:childPaneIsValidForSpecifier:contentDictionary:
当从这个方法返回false时,完成按钮将被禁用。请注意,contentDictionary
是一个可变字典。如果您更改某些值,UI将反映这些更改。这允许您自动纠正无效设置。
自定义视图
您可以在InAppSettingsKit中使用类型IASKCustomViewSpecifier
指定自己的UITableViewCell
。在这种情况下,必需的字段是Key
属性。此外,您必须支持IASKSettingsDelegate
协议并实现这些方法
- (CGFloat)settingsViewController:(UITableViewController<IASKViewController> *)settingsViewController heightForSpecifier:(IASKSpecifier *)specifier;
- (UITableViewCell*)settingsViewController:(UITableViewController<IASKViewController> *)settingsViewController cellForSpecifier:(IASKSpecifier*)specifier;
这两个方法都会为您的所有IASKCustomViewSpecifier
条目调用。为了区分它们,您可以使用specifier.key
访问Key
属性。在第一个方法中,您返回单元格的高度,在第二个方法中,您返回单元格本身。您应像在常规表格视图编程中一样使用可重复使用的UITableViewCell
对象。Demo应用中有一个示例。
可选您可实现:
- (void)settingsViewController:(IASKAppSettingsViewController*)settingsViewController
didSelectCustomViewSpecifier:(IASKSpecifier*)specifier;
来捕获自定义视图的点击事件。
如果您指定了File
、IASKViewControllerClass
、IASKViewControllerStoryBoardId
或IASKSegueIdentifier
(见下文),则自定义视图的选择行为与子面板相同,并且在选择时不会调用代理。
节标题和页脚
系统设置中提供了组元素FooterText键。它在InAppSettingsKit中也被支持。此外,我们还支持多值元素的这个键。页脚文本显示在多值选项表下方。
通过添加KEY属性并在您的IASKSettingsDelegate中实现以下方法,可以为PSGroupSpecifier段定义自定义标题视图:
- (UIView *)settingsViewController:(id<IASKViewController>)settingsViewController tableView:(UITableView *)tableView viewForHeaderForSection:(NSInteger)section;
通过实现以下方法调整标题的高度:
- (CGFloat)settingsViewController:(id<IASKViewController>)settingsViewController tableView:(UITableView*)tableView heightForHeaderForSection:(NSInteger)section;
如果您旨在不使用自定义视图来简化标题的定制,且未实现-settingsViewController:tableView:viewForHeaderForSection:
方法或该方法返回对应节的nil
值,则可实现以下方法:
- (NSString *)settingsViewController:(id<IASKViewController>)settingsViewController tableView:(UITableView*)tableView titleForHeaderForSection:(NSInteger)section;
如果方法返回nil
或一个长度为0的字符串,则使用在.plist中定义的标题。
该行为类似于自定义表格视图单元。在实现一个方法时,如果需要,可以方便地从其索引获取节键,如下所示:
NSString *key = [settingsViewController.settingsReader keyForSection:section];
查看示例应用程序以获取具体示例。
对于页脚定制,可以从IASKSettingsDelegate协议中实现三个类似的方法。
扩展子窗格
自定义视图控制器
对于子窗格元素(PSChildPaneSpecifier
),Apple要求一个file
键,该键指定子plist。InAppSettingsKit允许您选择性地指定IASKViewControllerClass
和IASKViewControllerSelector
。在这种情况下,通过实例化指定类的UIViewController子类并使用在IASKViewControllerSelector
中指定的初始化方法初始化它来显示子窗格。选择器必须有两个参数:一个用于设置包中文件名的NSString
参数和IASKSpecifier。然后,自定义视图控制器被推送到导航堆栈上。请参阅示例应用程序以获取更多详细信息。
使用从Storyboard中自定义ViewController
或者指定 IASKViewControllerStoryBoardId
从 主Storyboard 中启动ViewController。指定 IASKViewControllerStoryBoardFile
以使用应用的 Info.plist
中除主Storyboard之外的其他Storyboard。
执行跳转
作为 IASKViewControllerClass
和 IASKViewControllerSelector
对于子面板元素(PSChildPaneSpecifier
)的替代,InAppSettingsKit能够通过执行您Storyboard中定义的任何跳转来导航到另一个ViewController。为此,在 IASKSegueIdentifier
中指定跳转标识符。
扩展各种指定器
字幕
IASKSubtitle
键允许为以下元素定义字幕:切换、子面板、打开URL、邮件编辑、按钮。使用字幕意味着左对齐。如果指定了子面板的值,则子面板将显示为字幕,如果没有指定 IASKSubtitle
。字幕可以是一个可本地化的字符串或一个根据当前值包含可本地化字幕的字典。YES
和 NO
用作布尔切换值的键。字典可能包含一个 __default__
键,用于在没有匹配键时定义字幕。
UITextContentTypeEmailAddress
的文本框,请使用 IASKTextContentType
: EmailAddress
。
验证
)。示例:要配置具有 可以使用代理回调验证文本字段
- (IASKValidationResult)settingsViewController:(IASKAppSettingsViewController*)settingsViewController validateSpecifier:(IASKSpecifier*)specifier textField:(IASKTextField*)textField previousValue:(nullable NSString*)previousValue replacement:(NSString* _Nonnull __autoreleasing *_Nullable)replacement;
回调接收的是 IASKTextField
,它是 UITextField
的子类,允许在验证错误的情况下对文本字段进行样式设置(例如,红色文本)。它包含一个参数用于替换无效文本。返回 IASKValidationResultFailedWithShake
使得文本框震动以视觉上表示验证错误。
自定义切换
PSToggleSwitchSpecifier
开关默认使用 UISwitch
。通过指定选项 IASKToggleStyle
: Checkmark
,将显示所选键的勾选标记。
动态多值列表
多值列表(PSMultiValueSpecifier
)可以从委托那里动态获取其值和标题,而不是从静态的 Plist 获取。在你的 IASKSettingsDelegate
中实现这两个方法。
- (NSArray*)settingsViewController:(IASKAppSettingsViewController*)sender valuesForSpecifier:(IASKSpecifier*)specifier;
- (NSArray*)settingsViewController:(IASKAppSettingsViewController*)sender titlesForSpecifier:(IASKSpecifier*)specifier;
示例应用程序返回所有国家代码的列表作为值和本地化国家名称作为标题。
可以通过在 Plist 中添加一个布尔 DisplaySortedByTitle
键来按字母顺序排序多值列表。可以为多值列表条目分配一个图像。使用 IconNames
属性(位于值/标题/简称等旁边)指定图像。
设置存储
IASK默认将设置存储在[NSUserDefaults standardUserDefaults]
中。但是,可以通过在IASKAppSettingsViewController
上设置settingsStore
属性来改变此行为。IASK提供了两个存储实现:IASKSettingsStoreUserDefaults
(默认实现)和IASKSettingsStoreFile
,后者在您选择的路径的文件中读取和写入设置。如果您需要更具体的内容,您也可以选择创建自己的存储。创建自己的存储的最简单方法是创建IASKAbstractSettingsStore
的子类。只需覆盖3个方法。有关更多信息,请参阅IASKSettingsStore.{h,m}
。
通知
有一个IASKSettingChangedNotification
通知,每当有设置键变更时都会发送它。通知的object
是发送视图控制器,而userInfo
字典包含受影响键的键和新的值。
动态单元格隐藏
有时候,选项之间相互依赖。例如,您可能想要有一个“自动连接”开关,当开启时允许用户设置用户名和密码。要针对特定设置的更改做出反应,请使用上文所述的 IASKSettingChangedNotification
通知。
要隐藏一组单元格,使用
- (void)[IASKAppSettingsViewController setHiddenKeys:(NSSet*)hiddenKeys animated:(BOOL)animated];
或者非动画版本
@property (nonatomic, strong) NSSet *hiddenKeys;
请参阅样本应用以了解更多细节。在 hiddenKeys
中包含 PSGroupSpecifier
键可以隐藏整个部分。
注册默认值
设置属性列表支持 DefaultValue
参数,在 NSUserDefaults
中没有存储值的情况下显示默认值。然而,当应用程序查询 NSUserDefaults
的值时,默认值不会被传播。这是有意义的,因为 NSUserDefaults
不知道设置属性列表。
为了为各种设置键初始化值,NSUserDefaults
提供了 registerDefaults:
方法,该方法接受一个字典,该字典包含了“回退”值,当没有存储值时,由 NSUserDefaults
返回。这通常在应用程序启动时调用。
然而,创建和维护该字典可能会变得繁琐,并且存在这种字典和设置默认值不一致的风险。
为了解决这个问题,IASKSettingsReader
提供了一个方法,通过遍历 Root.plist 和所有子 plists 来生成这个字典,并收集所有键的 DefaultValue
。
NSDictionary *defaultDict = [appSettingsViewController.settingsReader gatherDefaultsLimitedToEditableFields:YES];
[NSUserDefaults.standardUserDefaults registerDefaults:defaultDict];
icloud 同步
要将 NSUserDefaults
与 iCloud 同步,还有一个名为 FTiCloudSync 的项目,它作为一个分类在 NSUserDefaults
上实现:所有写和删除请求都会自动转发到 iCloud,所有来自 iCloud 的更新都会自动存储在 NSUserDefaults
中。如果使用基于标准 NSUserDefaults
的存储,InAppSettingsKit 会自动更新 UI。
支持
请不要用 Github issues 提交支持请求,我们将关闭它们。相反,请在 StackOverflow 上发布带有标签 inappsettingskit
的问题。
授权
我们将代码以自由的 BSD 许可证发布,以便将其包含在任何项目中,无论是免费还是付费应用。我们唯一的要求是给予原始开发者一些认可。包含认可的最简单方法是保留“由 InAppSettingsKit 驱动”的通知。如果您决定删除此通知,在 App Store 描述页或主页上做出明显的提及也是可以的。
作者
最初由我的朋友 Lucas Vandal 开发,我接管了开发并继续更新该框架。如果您想支持我的开源工作,请考虑作为 赞助商 加入我!
Ortwin Gentz