This document provides the specification of the .plist file that specifies the layout of an iPhone preference pane.
Root level
The root level of the plist may contain these keys:
struct <preferences specifier plist> ::
key | type | meaning | depends | |
---|---|---|---|---|
title | string | localizable string | Title of the preference pane. | - |
items | array | ... of <entry>s | Array of specifier definitions. | - |
id | string | Specifier ID. | - |
Localization
Some strings, e.g. the title of the preference pane is localizable. Preferences.framework can localize these strings by looking for the corresponding key in localizationTable.strings in bundle. If you are writing a PreferenceBundles, the localizationTable is the name of the specifier plist and bundle is of course the PreferenceBundle itself.
For example, if the plist is named MySettings.plist, then the corresponding strings file should be named MySettings.strings.
Specifier Entries
You can use any keys that your controller recognizes in the plist for further customization. This table lists the internal ones:
struct <entry> ::
General keys
key | type | meaning | depends | |
---|---|---|---|---|
requiredCapabilities | array | ... of <capability>s | Required capabilities of the device such that this specifier can be shown. | - |
cell | string | <cell type> | Specifier cell type. | - |
label | string | localizable string | Label of specifier. | - |
id | string | Specifier ID. | - | |
get | string | selector | Getter. | - |
set | string | selector | Setter. | - |
action | string | selector | Action. | - |
enabled | boolean | Whether the control is enabled by default. | - | |
defaults | string | bundle ID | The user defaults associated with this specifier. | - |
key | string | Key of the user defaults. | defaults ≠ nil | |
default | any | Default value of control. | - | |
negate | boolean | If the key in the user defaults is a boolean, invert the value displayed. | - | |
PostNotification | string | Darwin Notification to post when the preference is changed. | - |
Here, cell must be one of:
- enum <cell type> ::
- PSGroupCell
- PSLinkCell
- PSLinkListCell
- PSListItemCell
- PSTitleValueCell
- PSSliderCell
- PSSwitchCell
- PSStaticTextCell
- PSEditTextCell
- PSSegmentCell
- PSGiantIconCell
- PSGiantCell
- PSSecureEditTextCell
- PSButtonCell
- PSEditTextViewCell
The cell type is actually determined from the class method +[PSTableCell cellTypeFromString:].
bundle-dependent keys
The following keys are meaningful when bundle is present. It is useful for loading extra resources and custom code.
key | type | meaning | depends | |
---|---|---|---|---|
bundle | string | filename | Bundle file name. This bundle will be loaded for additional resources. | - |
internal | boolean | Directory to search for the bundle. If true, search in /AppleInternal/Library/PreferenceBundles/. If false, search in /System/Library/PreferenceBundles/. |
bundle ≠ nil | |
isController | boolean | Whether the bundle contains a controller class. | bundle ≠ nil | |
overridePrincipalClass | boolean | Overrides the principal class by the detail controller when bundle has a controller. | isController = true | |
detail | string | class name | Detail controller class. | - |
pane | string | class name | Edit pane class. If bundle is absent, the edit pane class is obtained from the current bundle. Default value is PSEditingPane. |
detail ≠ nil |
hasIcon | boolean | Whether the specifier will have an icon. | bundle ≠ nil | |
icon | string | filename | File name of the icon to use. Default value is icon.png. The height of the icon should be 29px. | hasIcon = true |
cellClass | string | class name | Customized cell class | - |
customControllerClass | string | class name | Custom controller class to use when the view become visible | - |
get, set and action should respectively have signatures
-(id)getSomethingForSpecifier:(PSSpecifier*)spec;
-(void)setSomething:(id)something forSpecifier:(PSSpecifier*)spec;
-(void)speciferPerformedAction:(PSSpecifier*)spec;
where the type of "something" depends on the type of specifier, e.g. for a text field it should be an NSString, while for a switch it should be an NSNumber with boolean.
Of course, you can ignore extra parameters, e.g. -(void)specifierPerformedAction is a valid signature too.
Editing cells
These keys are specific to editing cells.
key | type | meaning | depends | |
---|---|---|---|---|
autoCaps | string | {"sentences", "words", "all"} | Autocapitalization type for cells that requires a keyboard. | - |
keyboard | string | {"numbers", "phone"} | Type of keyboard. | - |
prompt | string | localizable string | Setup prompt. | cell ∈ {"PSEditTextCell", "PSSecureEditTextCell"} |
okTitle | string | localizable string | Title for OK button in setup prompt. | prompt ≠ nil |
cancelTitle | string | localizable string | Title for cancel button in setup prompt. | prompt ≠ nil |
placeholder | string | localizable string | Placeholder. | cell ∈ {"PSEditTextCell", "PSSecureEditTextCell"} |
suffix | string | localizable string | Suffix. | cell ∈ {"PSEditTextCell", "PSSecureEditTextCell"} |
isIP | boolean | Input field intended for entering IP address (Use Numbers keyboard). | - | |
isURL | boolean | Input field intended for entering URL (Use URL keyboard). | - | |
isNumeric | boolean | Input field intended for entering numbers (Use NumberPad keyboard). | - | |
isEmail | boolean | Input field intended for entering e-mail (Use EmailAddress keyboard). | - | |
isEmailAdressing | boolean | ? | - | |
bestGuess | string | selector | Initial value of text field. | cell ∈ {"PSEditTextCell", "PSSecureEditTextCell"} |
noAutoCorrect | boolean | Disable auto-correction. | cell ∈ {"PSEditTextCell", "PSSecureEditTextCell"} |
List cells
These keys are specific to list cells.
key | type | meaning | depends | |
---|---|---|---|---|
validValues | array | ...of strings | List of values to choose from. | cell ∈ {"PSLinkListCell", "PSSegmentCell"} |
validTitles | array | ...of localizable strings | Titles corresponding to the list of values. | cell ∈ {"PSLinkListCell", "PSSegmentCell"} |
shortTitles | array | ...of localizable strings | Short titles. | cell ∈ {"PSLinkListCell", "PSSegmentCell"} |
valuesDataSource | string | selector | Selector to call to get the list of values dynamically. | cell ∈ {"PSLinkListCell", "PSSegmentCell"} and validValues = nil |
titlesDataSource | string | selector | Selector to call to get the list of titles dynamically. | cell ∈ {"PSLinkListCell", "PSSegmentCell"} and validTitles = nil |
staticTextMessage | string | localizable string | Static text message (?). | cell = "PSLinkListCell" |
valuesDataSource and titlesDataSource are performed on the target sent from -[PSListController loadSpecifiersFromPlistName:target:]. They must return an NSArray containing the values and (localized) titles respectively. Their signatures should be
-(NSArray*)dataFromTarget:(id)target;
Slider and switch cells
These keys are specific to slider and switch cells.
key | type | meaning | depends | |
---|---|---|---|---|
rightImage | string | filename | Image displayed next to the slider on the right. | cell = "PSSliderCell" |
leftImage | string | filename | Image displayed next to the slider on the left. | cell = "PSSliderCell" |
min | float | Minimum value of slider. | cell = "PSSliderCell" | |
max | float | Maximum value of slider. | cell = "PSSliderCell" | |
showValue | boolean | Show the value. | cell = "PSSliderCell" | |
alternateColors | boolean | Show the value. | cell = "PSSwitchCell" |
Miscellaneous control cells
These keys are specific to other control cells.
key | type | meaning | depends | |
---|---|---|---|---|
alignment | integer | Text alignment. 1 = center. | cell ∈ {"PSGroupCell", "PSStaticTextCell" | |
confirmation | dictionary | <confirmation> | Definitions of the confirmation sheet before action is performed. | cell ∈ {"PSSwitchCell", "PSButtonCell" |
isDestructive | boolean | Whether the action to be performed is destructive. The OK button will be in red if true. | confirmation ≠ nil | |
max | float | Maximum value of slider. | cell = "PSSliderCell" | |
showValue | boolean | Show the value. | cell = "PSSliderCell" | |
alternateColors | boolean | Show the value. | cell = "PSSwitchCell" | |
isStaticText | boolean | Whether the cells in this group has static text. Used in conjunction with PSStaticTextCell. | cell = "PSGroupCell" | |
height | float | Height of text view. | cell = "PSTextViewCell" | |
dontIndentOnRemove | boolean | ? | - | |
footerText | string | Text displayed in a small font after this specifier (or, in the case of a PSGroupCell, the last specifier in the group). | - | |
footerCellClass | class | The cell class using which to render the footer. | - |
Here, confirmation itself is a dictionary containing the following fields:
struct <confirmation> ::
key | type | meaning | |
---|---|---|---|
prompt | string | localizable string | Content of confirmation sheet. |
cancelTitle | string | localizable string | Title of the cancel button. |
okTitle | string | localizable string | Title of the OK button. |
title | string | localizable string | Title of confirmation sheet. |
PSSpecifier properties of plist keys
The tables above only shows the keys recognized by SpecifiersForPlist when translating the plist into an array of PSSpecifiers. They may be corresponds to the actual properties of the specifier. If you would like to generate a PSSpecifier in runtime, some actions may differ:
keys | corresponding action |
---|---|
cell | Use the constructor, or change the cellType ivar. |
label | Use the name declared property. |
get | Use the constructor, or change the getter ivar. |
set | Use the constructor, or change the setter ivar. |
action | Change the action ivar. |
default | Use the value property. |
icon | Use the iconImage as an UIImage, or -[PSSpecifier setupIconImageWithPath:] |
autoCaps, keyboard, noAutoCorrect | -[PSSpecifier setKeyboardType:autoCaps:autoCorrection:] |
isIP, isURL, isNumeric, isEmail, isEmailAddressing | Change the textFieldType ivar. |
bestGuess | Change the bestGuess ivar of the PSTextFieldSpecifier class. |
validValues, validTitles, shortTitles | -[PSSpecifier setValues:titles:shortTitles:] |
confirmation | Create an instance of PSConfirmationSpecifier. |
Recipes
Using PSLinkListCell
In order to make a PSLinkListCell actually work like a list, you must supply the key-value pair
detail = "PSListItemsController";
also.
Using PSLinkCell
PSLinkCell is useful for linking to sub-preference-panes. The simplest example just needs 2 keys:
{ cell = PSLinkCell; label = "Settings-iPhone"; }
The label is the important part. When user clicked on the link cell, iPhoneOS will use the unlocalized label as the file name of the plist for the next pane. For example in above, the main settings screen will appear.
If you use just 2 keys, only plists inside Preferences can be loaded. In order to load your own plist, you must use a custom subclass of PSListController in detail:
{ cell = PSLinkCell; label = "My Awesome Pane"; detail = MyListController; }
MyListController can simply be an empty subclass of PSListController:
@interface MyListController : PSListController {}
@end
@implementation MyListController
@end
The key thing is when you place MyListController inside your bundle, its bundle property will return your bundle which My Awesome Pane.plist can be found.
Constructing PSLinkCell in Run time
If you want to dynamically add a specifier for a PSLinkCell linking to a bundle, do it like this:
PSSpecifier* specifier = [PSSpecifier preferenceSpecifierNamed:@"title"
target:self
set:NULL
get:NULL
detail:Nil
cell:PSLinkCell
edit:Nil];
NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/PreferenceBundles/prefs.bundle"];
[specifier setProperty:bundle forKey:@"lazy-bundle"];
specifier->action = @selector(lazyLoadBundle:);
// Add specifier to the PSListController
Making a red delete button
The red delete button in VPN is in fact very easy to implement. All you need to do is add the following code:
#import <UIKit/UIPreferencesDeleteTableCell.h>
@interface PSDeleteTableCell : UIPreferencesDeleteTableCell @end
@implementation PSDeleteTableCell
-(void)setValueChangedTarget:(id)target action:(SEL)action userInfo:(NSDictionary*)info {
[self setTarget:target];
[self setAction:action];
}
-(UILabel*)titleTextLabel {
UILabel* res = [super titleTextLabel];
res.textColor = [UIColor whiteColor];
return res;
}
@end
and then in the specifier plist, modify your button as:
... { cell = PSButtonCell; action = nukeFromOrbit; label = "Nuke from Orbit; ... cellClass = PSDeleteTableCell; }, ...
Making a custom cell, header or footer is useful because it allows you to customize the style, add an image, etc.
All you need to do is make a class that looks like:
@interface CustomCell : PSTableCell <PreferencesTableCustomView> {
UILabel *_label;
}
@end
@implementation CustomCell
- (id)initWithSpecifier:(PSSpecifier *)specifier
{
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell" specifier:specifier];
if (self) {
CGRect frame = [self frame];
_label = [[UILabel alloc] initWithFrame:frame];
[_label setLineBreakMode:UILineBreakModeWordWrap];
[_label setNumberOfLines:0];
[_label setText:@"You can use attributed text to make this prettier."];
[_label setBackgroundColor:[UIColor clearColor]];
[_label setShadowColor:[UIColor whiteColor]];
[_label setShadowOffset:CGSizeMake(0,1)];
[_label setTextAlignment:UITextAlignmentCenter];
[self addSubview:_label];
[_label release];
}
return self;
}
- (float)preferredHeightForWidth:(float)arg1
{
// Return a custom cell height.
return 60.f;
}
@end
Then, set the cellClass
, headerCellClass
or footerCellClass
in your specifier. For example:
... { cell = PSGroupCell; footerCellClass = CustomFooterCell; }, ...
A cell
doesn't have to be specified for custom cells.