Sync Mnemosyne
Sync Mnemosyne

Reputation: 173

TableView in iOS Settings Bundle

I am a newbie on iOS Development and I am sorting out how am I going to have a table view with a checkmark on the settings bundle like this:

enter image description here

Is there any way to do it? Or is this just only available for specific iOS approved apps?

Looking forward for an answer. Thanks.

Upvotes: 0

Views: 929

Answers (3)

Shamsudheen TK
Shamsudheen TK

Reputation: 31311

You can achieve this through Multi Value Element. When the user taps a preference containing a multi-value element, the Settings application displays a new page with the possible values to choose from. Google it for the tutorials if needed (Multivalue option for the settings bundle).

Here are the details : PSMultiValueSpecifier

Upvotes: 2

dheeru
dheeru

Reputation: 395

#import "SettingViewController.h"
NSInteger selectedIndex;
//Use this, it works for me 

- (void)viewDidLoad
{
selectedIndex = 0;
    [super viewDidLoad];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 50.0f;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [_arrayForSaveSetting count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = nil;

    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
    if (indexPath.row==selectedIndex) {
        cell.accessoryType =UITableViewCellAccessoryCheckmark;
    }

    return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    selectedIndex=indexPath.row;   
    [_tblSaveSetting reloadData];
}

Upvotes: 0

freshking
freshking

Reputation: 1864

If I am guessing right and you want to know how to display settings outside your app and in the iOS Settings, then check out this tutorial. It should get you started.

Taken out of the link below:

I have been searching around and couldn't find a boilerplate solution so created my own code for doing this. It supports the setting types Title, Group, Text Field, Multi Value and Toggle Switch.

It does NOT SUPPORT Slider.

This solution does support portrait AND landscape mode and can also handle changing over device orientations.

First off all I'm assuming that you are using the following code to read out your default values from the Settings.bundle.

- (void) registerDefaultsFromSettingsBundle
{
    NSLog(@"Registering default values from Settings.bundle");
    NSUserDefaults * defs = [NSUserDefaults standardUserDefaults];
    [defs synchronize];

    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)
        {
            // check if value readable in userDefaults
            id currentObject = [defs objectForKey: key];
            if (currentObject == nil)
            {
                // not readable: set value from Settings.bundle
                id objectToSet = [prefSpecification objectForKey: @"DefaultValue"];
                [defaultsToRegister setObject: objectToSet forKey: key];
                NSLog(@"Setting object %@ for key %@", objectToSet, key);
            }
            else
            {
                // already readable: don't touch
                NSLog(@"Key %@ is readable (value: %@), nothing written to defaults.", key, currentObject);
            }
        }
    }

    [defs registerDefaults: defaultsToRegister];
    [defs synchronize];
}

Okay now you'll need 2 classes. SettingsTableViewController and MultiValueTableViewController.

SettingsTableViewController.h

    //
//  SettingsTableViewController.h
//  Cochlear App
//
//  Created by Gilles Lesire on 16/07/14.
//  Free to use
//

#import <UIKit/UIKit.h>
#import "MultiValueTableViewController.h"

@interface SettingsTableViewController : UITableViewController <MultiValueDelegate> {
    NSMutableArray *labelViews;
    NSMutableArray *textViews;
    NSMutableArray *settingsKeys;
    NSMutableArray *settingsTableSections;
    NSMutableArray *settingsTableData;
}

@end

SettingsTableViewController.m

//
//  SettingsTableViewController.m
//  Cochlear App
//
//  Created by Gilles Lesire on 16/07/14.
//  Free to use
////

#import "SettingsTableViewController.h"

#define labelCGRectX 25
#define labelCGRectY 25
#define labelCGRectWidth 140
#define labelCGRectHeight 21

#define typeGroup @"PSGroupSpecifier"
#define typeTitle @"PSTitleValueSpecifier"
#define typeToggleSwitch @"PSToggleSwitchSpecifier"
#define typeMultiValue @"PSMultiValueSpecifier"
#define typeTextField @"PSTextFieldSpecifier"

@interface SettingsTableViewController ()

@end

@implementation SettingsTableViewController

- (id)initWithStyle: (UITableViewStyle)style
{
    self = [super initWithStyle: style];

    if (self) {
    }

    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Track rotation changes
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(deviceOrientationDidChange) name: UIDeviceOrientationDidChangeNotification object: nil];

    // Avoid tab bar to overlap tableview
    self.edgesForExtendedLayout = UIRectEdgeAll;
    self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);

    // Custom initialization
    labelViews = [NSMutableArray arrayWithObjects: nil];
    textViews = [NSMutableArray arrayWithObjects: nil];
    settingsTableSections = [NSMutableArray arrayWithObjects: nil];
    settingsTableData = [NSMutableArray arrayWithObjects: nil];
    settingsKeys = [NSMutableArray arrayWithObjects: nil];

    NSLog(@"Created arrays");


    NSString *settingsBundle = [[NSBundle mainBundle] pathForResource: @"Settings" ofType: @"bundle"];

    if(!settingsBundle) {

        NSLog(@"Could not find Settings.bundle");

    } else {

        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) {

            NSLog(@"%@", prefSpecification);

            NSString *title = [prefSpecification objectForKey: @"Title"];
            NSString *type = [prefSpecification objectForKey: @"Type"];

            if([type isEqualToString: typeGroup]) {

                // Create new section
                [settingsTableSections addObject: title];
                NSMutableArray *newSection = [NSMutableArray arrayWithObjects: nil];
                [settingsTableData addObject: newSection];

            } else {

                // Add specification to last section
                [[settingsTableData objectAtIndex: ([settingsTableData count] - 1)] addObject:prefSpecification];

            }
        }
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
    // Return the number of sections.
    return [settingsTableSections count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [[settingsTableData objectAtIndex: section] count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return [settingsTableSections objectAtIndex: section];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    // Get the dictionary item
    NSDictionary *prefSpecification = [[settingsTableData objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];

    NSString *title = [prefSpecification objectForKey: @"Title"];
    NSString *key = [prefSpecification objectForKey: @"Key"];
    NSString *type = [prefSpecification objectForKey: @"Type"];

    // Define cell
    UITableViewCell *cell;

    // Keep tag of keys
    [settingsKeys addObject: key];
    int tag = [settingsKeys count] - 1;

    if([type isEqualToString: typeTitle]) {

        // Create cell
        cell = [tableView dequeueReusableCellWithIdentifier: @"Cell" forIndexPath:indexPath];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        // Set title
        cell.textLabel.text = title;

        // Add label
        UILabel *labelView = [[UILabel alloc] initWithFrame: CGRectMake(labelCGRectX, labelCGRectY, labelCGRectWidth, labelCGRectHeight)];
        labelView.text = [[NSUserDefaults standardUserDefaults] objectForKey: key];
        labelView.textAlignment = NSTextAlignmentRight;
        labelView.textColor = [UIColor grayColor];
        cell.accessoryView = labelView;

    }
    if([type isEqualToString: typeToggleSwitch]) {

        // Create cell
        cell = [tableView dequeueReusableCellWithIdentifier: @"Cell" forIndexPath:indexPath];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        // Set title
        cell.textLabel.text = title;

        // Add switch
        UISwitch *switchView = [[UISwitch alloc] initWithFrame: CGRectZero];
        cell.accessoryView = switchView;
        switchView.tag = tag;
        [switchView setOn: [[[NSUserDefaults standardUserDefaults] objectForKey: key] boolValue] animated: NO];

        // Connect action to switch
        [switchView addTarget: self action: @selector(switchChanged:) forControlEvents:UIControlEventValueChanged];

    }
    if([type isEqualToString: typeTextField]) {

        // Create cell
        cell = [tableView dequeueReusableCellWithIdentifier: @"Cell" forIndexPath:indexPath];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        int frameSize = self.view.frame.size.width;

        UITextField *textField = [[UITextField alloc] initWithFrame: CGRectMake(15, 10, frameSize,labelCGRectHeight)];
        textField.tag = tag;
        textField.text = [[NSUserDefaults standardUserDefaults] objectForKey: key];

        [textField addTarget: self
                      action: @selector(textFieldChanged:)
            forControlEvents: UIControlEventEditingChanged];

        [cell.contentView addSubview: textField];

        // Tract text field
        [textViews addObject: textField];
    }
    if([type isEqualToString: typeMultiValue]) {

        // Create cell
        cell = [tableView dequeueReusableCellWithIdentifier: @"MultiValueCell" forIndexPath:indexPath];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        // Get value
        int value = [[[NSUserDefaults standardUserDefaults] objectForKey: key] intValue];
        NSArray *values = [prefSpecification objectForKey: @"Values"];
        NSArray *titles = [prefSpecification objectForKey: @"Titles"];
        NSString *multiValue = @"Unknown";
        int index = [values indexOfObject: [NSString stringWithFormat: @"%d", value]];

        if(index >= 0 && index < [values count]) {
            multiValue = [titles objectAtIndex: index];
        }

        // Set title
        cell.textLabel.text = title;

        int frameSize = self.view.frame.size.width;

        // Add label
        UILabel *labelView = [[UILabel alloc] initWithFrame: CGRectMake((frameSize - labelCGRectWidth - 30), 12, labelCGRectWidth, labelCGRectHeight)];
        labelView.textAlignment = NSTextAlignmentRight;
        labelView.text = multiValue;
        labelView.textColor = [UIColor grayColor];

        [cell addSubview: labelView];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

        // Track label
        [labelViews addObject: labelView];
    }

    return cell;
}

- (void) switchChanged: (id) sender {
    UISwitch* switchControl = sender;

    NSString *key = [settingsKeys objectAtIndex: switchControl.tag];
    NSNumber *numberValue = [NSNumber numberWithBool: switchControl.on];

    [[NSUserDefaults standardUserDefaults] setObject: numberValue forKey: key];
}
- (void) textFieldChanged: (id) sender {
    UITextField* textControl = sender;

    NSString *key = [settingsKeys objectAtIndex: textControl.tag];
    NSString *stringValue = textControl.text;

    [[NSUserDefaults standardUserDefaults] setObject: stringValue forKey: key];
}

- (void) selectedMultiValue {
    [self reloadTable];
}

- (void) deviceOrientationDidChange {
    [self reloadTable];
}

- (void)prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
    if ([[segue identifier] isEqualToString: @"changeMultiValue"])
    {
        MultiValueTableViewController *multiValueViewController =
        [segue destinationViewController];

        NSIndexPath *indexPath = [self.tableView
                                  indexPathForSelectedRow];

        // Get the dictionary item
        NSDictionary *prefSpecification = [[settingsTableData objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];

        multiValueViewController.prefSpecification = prefSpecification;
        multiValueViewController.delegate = self;
    }
}

- (void) reloadTable {
    for (UILabel *labelView in labelViews) {
        [labelView removeFromSuperview];
    }
    for (UITextField *textView in textViews) {
        [textView removeFromSuperview];
    }

    // Remove references to objects for garbage collection
    labelViews = [NSMutableArray arrayWithObjects: nil];
    textViews = [NSMutableArray arrayWithObjects: nil];

    [self.tableView reloadData];
}

- (void) dealloc {
    // Remove observers
    [[NSNotificationCenter defaultCenter] removeObserver: self];
}
@end

MultiValueTableViewController.h

//
//  MultiValueTableViewController.h
//  Cochlear App
//
//  Created by Gilles Lesire on 16/07/14.
//  Free to use
//

#import <UIKit/UIKit.h>
#import "SettingsController.h"

@protocol MultiValueDelegate

- (void) selectedMultiValue;

@end

@interface MultiValueTableViewController : UITableViewController {
    NSDictionary *prefSpecification;
}

@property (nonatomic) id<MultiValueDelegate> delegate;
@property (strong, nonatomic) NSDictionary *prefSpecification;

@end

MultiValueTableViewController.m

//
//  MultiValueTableViewController.m
//  Cochlear App
//
//  Created by Gilles Lesire on 16/07/14.
//  Free to use
//

#import "MultiValueTableViewController.h"

@interface MultiValueTableViewController ()

@end

@implementation MultiValueTableViewController

@synthesize prefSpecification;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSString *title = [prefSpecification objectForKey: @"Title"];
    self.navigationItem.title = title;

// Avoid tab bar to overlap tableview
self.edgesForExtendedLayout = UIRectEdgeAll;
self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSArray *values = [prefSpecification objectForKey: @"Values"];

    // Return the number of rows in the section.
    return [values count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: @"Cell" forIndexPath:indexPath];

    NSString *key = [prefSpecification objectForKey: @"Key"];
    NSArray *titles = [prefSpecification objectForKey: @"Titles"];
    NSArray *values = [prefSpecification objectForKey: @"Values"];
    NSString *title = [titles objectAtIndex: indexPath.row];

    // Create cell
    cell.selectionStyle = UITableViewCellSelectionStyleGray;

    // Set title
    cell.textLabel.text = title;

    // If this is the selected value
    if([[values objectAtIndex: indexPath.row] intValue] == [[[NSUserDefaults standardUserDefaults] objectForKey: key] intValue]) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *key = [prefSpecification objectForKey: @"Key"];
    NSArray *values = [prefSpecification objectForKey: @"Values"];

    NSNumber *value = [values objectAtIndex: indexPath.row];

    [[NSUserDefaults standardUserDefaults] setObject: value forKey: key];

    [self.delegate selectedMultiValue];
    [self.tableView reloadData];
}

@end

Storyboard Now go the storyboard and create a TableViewController. Select the TableViewController and Choose "Editor" -> "Embed in" -> "Navigation controller".

Set the class of the TableViewController as SettingsTableViewController. Set the identifier of the cell as "Cell", add a second TableViewCell to the TableView and set it's identifier as "MultiValueCell". Add a second TableViewController, and CTRL+CLICK and drag from the MultiValueCell to the second TableViewController. Set the class of the second TableViewController as MultiValueTableViewController. Set the identifier of the cell in the second TableViewController as "Cell" too. That's it!

Upvotes: 0

Related Questions