3 Разработка Mac-приложений на примерах. PreferencePanes

Плагины устанавливаются в папку PreferencePanes.

Mac OS X в будущем не будет поддерживать 32-bit.


1. Создадим новый проект с именем NewDefaults из раздела System Plug-in по шаблону Preference Pane.
2. Сохраним проект в папку projects в Documents
3. После создания проекта в нем будут файлы типа:
.tiff
Tagged Image File Format (tiff).
NewDefaults.tiff - иконка (32 x 32), которая будет отображаться в System Preferences.

.prefPane
Preference pane.
NewDefaults.prefPane - продукт сборки.

4. Если после сборки открыть файл NewDefaults.prefPane, то будет предложено установить его.

5. В настройках TARGETS в секции Info опция Preference Pane icon file содержит наименование файла с иконкой только без расширения например, NewDefaults
Опция Copyright (human- readable) может содержать имя разработчика или компании, например, Copyright © 2014 Devtype Software. All rights reserved.

6. При повторном запуске продукта сборки будет предложена переустановка.

7. Создадим 3 Check Box-a:
  • Enabling and disabling Launchpad fading
  • Showing and hiding hidden files in the Finder
  • Showing and hiding the Library folder in the current user's home folder
8. В NewDefaults.h добавляем символы, переменные, и методы:

#import <PreferencePanes/PreferencePanes.h>

@interface NewDefaults : NSPreferencePane

// Define the path to the Library folder
// in the users home folder
#define D_HOME_LIBRARY_PATH @"~/Library"
// Define the path to the defaults command
#define D_DEFAULTS_PATH @"/usr/bin/defaults"
// Define the read command
#define D_READ_COMMAND  @"read"
// Define the Finders domain
#define D_DOMAIN_FINDER @"com.apple.finder"
// Define the Docks domain
#define D_DOMAIN_DOCK @"com.apple.dock"
// Define the defaults command to show
// hidden files in the Finder
#define D_DEFAULTS_SHOW_FILES \
"defaults write com.apple.finder AppleShowAllFiles YES"
// Define the defaults command to hide
// hidden files in the Finder
#define D_DEFAULTS_HIDE_FILES \
"defaults write com.apple.finder AppleShowAllFiles NO"
// Define the defaults command to delete
// the Finder hidden files key
#define D_DEFAULTS_DEL_HIDE_FILES \
"defaults delete com.apple.finder AppleShowAllFiles"
// Define the defaults commands to enable
// Launchpad fading
#define D_DEFAULTS_DEL_SHOW_DURATION \
"defaults delete com.apple.dock springboard-show-duration"
#define D_DEFAULTS_DEL_HIDE_DURATION \
"defaults delete com.apple.dock springboard-hide-duration"
// Define the defaults commands to disable
// Launchpad fading
#define D_DEFAULTS_SHOW_DURATION_0 \
"defaults write com.apple.dock \
springboard-show-duration -int 0"
#define D_DEFAULTS_HIDE_DURATION_0 \
"defaults write com.apple.dock \
springboard-hide-duration -int 0"
// Define the command to restart
// the Finder
#define D_RESTART_FINDER "killall Finder"
// Define the command to restart
// the Dock
#define D_RESTART_DOCK "killall Dock"
// Define the key to show / hide hidden files
#define kHiddenFileKey @"AppleShowAllFiles"
// Define the key to disable Launchpad fading
#define kSpringboardShowTime @"springboard-show-duration"
#define kSpringboardHideTime @"springboard-hide-duration"
// Define a string equal to YES
#define D_YES @"YES"

{
    /*
     Create the user interface elements
     */
    // Buttons - A Check Box is a button
    IBOutlet NSButton* enableHiddenFilesCheckbox;

    IBOutlet NSButton* enableLaunchpadFadeCheckbox;

    IBOutlet NSButton* enableHomeLibraryCheckbox;
}
- (void)mainViewDidLoad;

/*
 Create the Preference Pane interface for the buttons
 */
- (IBAction)myButtonAction:(id)sender;

/*
 Define the interface to a method that will use
 the defaults command to retrieve a domain
 defaults value for a key
 */
- (NSString *) readDefaults: (NSString *)a_domain
                     forKey: (NSString *)a_key;


@end

Здесь мы не использовали @property и @synthesize поэтому Xcode не создаст accessor методы автоматически. Это также означает что нужно обернуть в фигурные скобки объявление переменных экземпляра.

9. В NewDefaults.m добавляем:

#import "NewDefaults.h"

@implementation NewDefaults

/*
 Create the Preference Pane implementation for when
 a checkbox is clicked this method is called with the
 sender equal to the check box that invoked it
 */
- (IBAction)myButtonAction:(id)sender;
{
    // Disable sudden termination, it is not
    // safe to kill the System Preferences
    [[NSProcessInfo processInfo] disableSuddenTermination];
    
    // Was the Show ~/Library Folder button pressed?
    if (YES == [sender isEqual: enableHomeLibraryCheckbox])
    {
        // Get a URL reference to the file
        NSURL *l_url =
        [NSURL fileURLWithPath:
         [D_HOME_LIBRARY_PATH stringByExpandingTildeInPath]];
        // If the checkbox is ON (checked) then
        if (NSOnState == [sender state])
        {
            // Set the URL IsHidden value to NO
            // This makes the folder Visible in the Finder
            [l_url setResourceValue:
             [NSNumber numberWithBool:NO]
                             forKey:NSURLIsHiddenKey
                              error:Nil];
        }
        // else the checkbox is OFF (unchecked)
        else
        {
            // Set the URL IsHidden value to YES
            // This makes the folder Invisible in the Finder
            [l_url setResourceValue:
       
             [NSNumber numberWithBool:YES]
                             forKey:NSURLIsHiddenKey
                              error:Nil];
        }
    }
    
    // Was the Show Hidden Files in Finder button pressed?
    if (YES == [sender isEqual: enableHiddenFilesCheckbox])
    {
        // If the checkbox is ON (checked) then
        if (NSOnState == [sender state])
        {
            // Execute the command to enable showing hidden files
            system(D_DEFAULTS_SHOW_FILES);
        }
        // else the checkbox is OFF (unchecked) then
        else
        {
            // Execute the command to disable showing hidden files
            system(D_DEFAULTS_HIDE_FILES);
        }
        // Execute the system command to restart the Finder
        system(D_RESTART_FINDER);
    }
    
    // Was the Enable Launchpad fading button pressed?
    if (YES == [sender isEqual: enableLaunchpadFadeCheckbox])
    {
        // If the checkbox is ON (checked) then
        if (NSOnState == [sender state])
        {
            // Execute the commands to enable fading
            system(D_DEFAULTS_DEL_SHOW_DURATION);
            system(D_DEFAULTS_DEL_HIDE_DURATION);
        } else {
            // Execute the commands to disable fading
            system(D_DEFAULTS_SHOW_DURATION_0);
            system(D_DEFAULTS_HIDE_DURATION_0);
        }
        // Execute the system command to restart the Finder
        system(D_RESTART_DOCK);
    }
    
    // Enable sudden termination, it is
    // safe to kill the System Preferences
    [[NSProcessInfo processInfo] enableSuddenTermination];
}

- (void)mainViewDidLoad
{
}

/*
 This method will execute the command line tool defaults
 to read a value from a domain for a specific key
 Input:
 a_domain - a reverse domain id for the defaults to read
 a_key - a key value to read
 Output:
 Either the string value of the result or Nil if there
 is no value to return.
 */
- (NSString *) readDefaults: (NSString *)a_domain
                     forKey: (NSString *)a_key
{
    // Create a string reference to hold the result
    NSString *l_result;
    
    // Create and initialize a new task
    NSTask *l_readTask = [[NSTask alloc] init];
    
    // Assign the launch path for the task
    [l_readTask setLaunchPath:D_DEFAULTS_PATH];
    
    // Create an array with the read command
    // and the domain and key values
    NSArray *l_arguments =
    [NSArray arrayWithObjectsD_READ_COMMAND,
     a_domain,
     a_key,
     nil];
    // Assign the argument array to the NSTask
    [l_readTask setArguments:l_arguments];
    
    // Create a new pipe to read the results
    // of the command
    NSPipe *l_pipe = [NSPipe pipe];
    // Assign the read pipe to the NSTask
    [l_readTask setStandardOutput:l_pipe];
    // Retrieve the pipe's fileHandleForReading
    NSFileHandle *l_file = [l_pipe fileHandleForReading];
    
    // Launch the Task
    [l_readTask launch];
    // Release the Task - this is not needed on
    // Mac OS X 10.7 but is also harmless
    // [l_readTask release];
    
    // Read all the output data from the Task
    // and create a mutable copy
    // The NSTask will exit when done
    NSMutableData *l_data =
    [[l_file readDataToEndOfFile] mutableCopy];
    
    // If the NSTask ended but returned
    // nothing, then return a Nil
    if (0 == [l_data length])
    {
        return Nil; }
    
    // The defaults command will append a newline (\n)
    // character to its output.  This trims that
    // character off so that it is not included in
    // the returned value
    [l_data setLength:[l_data length] - 1];
    
    // Create a new string from the remaining
    // returned data
    l_result = [[NSString alloc] initWithData:l_data
                                     encoding:NSUTF8StringEncoding];
    // Return the final string
    return l_result;
}

/*
 The didSelect delegate method is called whenever
 the Preference Pane is displayed, even if the
 System Preferences were not quit and relaunched
 */
- (void) didSelect
{
    // Read the current setting for
    // Showing Hidden files in he Finder
    NSString *l_showHiddenFile = [self readDefaults: D_DOMAIN_FINDER forKey:kHiddenFileKey];
    
    // Read the current setting for
    // Disabling launchpad fading
    NSString *l_launchpadFadeOut =
    [self readDefaults: D_DOMAIN_DOCK
                forKey:kSpringboardHideTime];
    NSString *l_launchpadFadeIn =
    [self readDefaults: D_DOMAIN_DOCK
                forKey:kSpringboardShowTime];
    
    // Assume everything is off, later we will
    // turn the correct things back on
    [enableHiddenFilesCheckbox setState: NSOffState];
    [enableLaunchpadFadeCheckbox setState: NSOffState];
    [enableHomeLibraryCheckbox setState: NSOffState];
    
    // If the show hidden files value was found
    if (l_showHiddenFile)
    {
        // If the show hidden files value is YES
        if ([D_YES isEqual: l_showHiddenFile])
        {
            // Turn the check box on in the
            // interface
            [enableHiddenFilesCheckbox setState: NSOnState];
        } else {
            // Execute the system command to delete
            // the setting
            system(D_DEFAULTS_DEL_HIDE_FILES);
        } }
    
    // If both launchpad values were found
    if ((l_launchpadFadeOut) &&(l_launchpadFadeIn))
    {
        // If either launchpad values is not equal to 0
        if ((0 != [l_launchpadFadeOut integerValue]) ||
            (0 != [l_launchpadFadeIn integerValue]))
        {
            // Turn the check box on in the
            // interface
            [enableLaunchpadFadeCheckbox setState: NSOnState];
        }
    }
    else {
        // Turn the check box on in the
        // interface
        [enableLaunchpadFadeCheckbox setState: NSOnState];
    }
    
    // Create a URL object that references the
    // Library file in the users home directory
    NSURL *l_url = [NSURL fileURLWithPath:   [D_HOME_LIBRARY_PATH
                                              stringByExpandingTildeInPath]];
    
    // Define a NSNumber pointer to hod the result
    // of the getResourceValue method
    NSNumber  *l_isHidden;
    
    // Get the value of the IsHidden attribute
    BOOL l_result = [l_url getResourceValue:&l_isHidden
                                     forKey:NSURLIsHiddenKey error:Nil];
    
    // If a value was returned
    if (l_result)
    {
        // If the returned value was NO
        if (NO == [l_isHidden boolValue])
        {
            // Turn the check box on in the
            // interface
            [enableHomeLibraryCheckbox setState: NSOnState];
        }
    }
    
}


@end

Все NS объекты являются immutable (unchangeable) если они в своем названии не содержат Mutable. Чтобы получить mutable объект из inmutable надо ему послать сообщение mutableCopy.

10. Выбираем NewDefaults.xib, далее выбираем File's Owner, и в Custom Class вбиваем NewDefaults

11. Все checkbox-ы ПКМ перетягиваем на File's Owner и выбираем myButtonAction

12. Далее наоборот File's Owner ПКМ перетягиваем на checkbox-ы и выбираем соответствующие им IBOutlet-ы.