Preferences specifier plist

From iPhone Development Wiki
Jump to: navigation, search

This document provides the specification of the .plist file that specifies the layout of an iPhone preference pane.

Contents

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. -
height float Height of the cell. -
dontIndentOnRemove boolean Prevents the cell from being indented when the table view is in editing mode. -
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
    • PSSpinnerCell

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). -
isEmailAddressing 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 The message to be added below the cells list of PSLinkListCell. 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 Use an orange switch instead of blue. cell = "PSSwitchCell", iOS < 7
control-loading boolean Show an activity indicator view instead of switch (e.g. VPN cell) cell = "PSSwitchCell"

Colored Switch Cells in iOS 7

Since iOS 7.0 and above the key "alternateColors" does not work anymore. If you still want to change the Color of your UISwitch, you can accomplish this but not as easy as using the "alternateColors" key. To be able to change the Color of your Switches, you must subclass PSSwitchTableCell and therefore you must create a Preference Bundle.

We will create our subclass as following:

#import <Preferences/Preferences.h>
#import <Preferences/PSSwitchTableCell.h> //if this header is not found, update your headers or uncomment the part below
 
/*
Only uncomment this if you have older headers
 
@interface PSTableCell : UITableViewCell
@end
@interface PSControlTableCell : PSTableCell
-(UIControl *)control;
@end
@interface PSSwitchTableCell : PSControlTableCell
-(id)initWithStyle:(int)arg1 reuseIdentifier:(id)arg2 specifier:(id)arg3 ;
@end
*/
 
@interface SRSwitchTableCell : PSSwitchTableCell //our class
@end
 
@implementation SRSwitchTableCell
 
-(id)initWithStyle:(int)arg1 reuseIdentifier:(id)arg2 specifier:(id)arg3 { //init method
 
	self = [super initWithStyle:arg1 reuseIdentifier:arg2 specifier:arg3]; //call the super init method
 
	if (self) {
 
		[((UISwitch *)[self control]) setOnTintColor:[UIColor redColor]]; //change the switch color you can use any UIColor you want.
 
	}
 
	return self;
 
}
 
@end

That is all for our subclass, all you need to do now when creating SwitchCells is to set the Switch's Class to the created subclass:

<key>cellClass</key>
<string>SRSwitchTableCell</string>

This is all to get Colored Switches on iOS 7. If you add different identifiers to your PSSpecifiers, you can have different colors for each Switch.

A detailed Tutorial about this has been written sharedroutine.com.

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
isStaticText boolean Whether the cells in this group has static text. Used in conjunction with PSStaticTextCell. cell = "PSGroupCell"
footerText string Text displayed in a small font after this specifier (or, in the case of a PSGroupCell, the last specifier in the group). -
headerCellClass class The cell class using which to render the header. -
footerCellClass class The cell class using which to render the footer. -

footerAlignment: 1 centers the footer text (doesn't always look good for multiple lines)

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 -[PSSpecifier setProperty:forKey:] Use the iconImage and UIImage as a key and property, 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 PSSpinnerCell

PSSpinnerCell is a simple cell with an animating spinner (UIActivityIndicatorView) inside it. Available only in iOS 7. (or higher) There is no way to start or stop the animation manually, it is always running. Therefore, this cell is meant to be dynamically inserted and deleted from a PSListController subclass. PSSpinnerCell supports text and icons as well.

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 a PSLinkCell at runtime

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;
},
...

Note that UIPreferencesDeleteTableCell no longer exists as of iOS 6.0. It appears to be replaced by a boolean on PSButtonCell, isDestructive.

Making a custom cell, header or footer

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 = CustomCell;
},
...

A cell doesn't have to be specified for custom cells.

Making an editable PSListController

Making a PSListController editable, so you can preform "swipe-to-delete" on the rows is easy at runtime.

You just have to use a subclass of PSEditableListController (which is a subclass of PSListController) instead of a subclass of PSListController for your List Controller. For example:

@interface MyListController : PSEditableListController {}
@end
@implementation MyListController
- (id)specifiers
{
    if (!_specifiers){
       //add a sample specifier to the list
        PSSpecifier* testSpecifier = [PSSpecifier preferenceSpecifierNamed:@"test"
                                                               target:self
                                                                  set:nil
                                                                  get:nil
                                                               detail:nil
                                                                 cell:PSTitleValueCell
                                                                 edit:0];
        _specifiers = [NSArray arrayWithObjects:testSpecifier, nil];
 
    }
    return _specifiers;
}
@end

Please note, you will only be able to delete a specifier, if it's class is either PSLinkListCell, PSListItemCell or PSTitleValueCell

To perform a custom action when the specifier gets deleted, you have to implent a method in your PSEditableListController subclass like this one

-(void)removedSpecifier:(PSSpecifier*)specifier{
    UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"Removing specifier: "
                                                   message:[specifier name]
                                                  delegate:self
                                         cancelButtonTitle:@"OK"
                                         otherButtonTitles:nil];
    [alert show];
}

and set the deletionAction Key of the specifier:

extern NSString* PSDeletionActionKey;
 
/*construct the PSSpecifier* testSpecifier here
....
*/
 
[testSpecifier setProperty:NSStringFromSelector(@selector(removedSpecifier:)) forKey:PSDeletionActionKey];

References