Musings on Objective-C, in no particular order

Latest

Working with TableViews – Part 2

Creating Table Views and Navigation Controllers

If you want a table view to provide the main interface and navigation for your application in the same style as the contacts or settings apps, you need to implement a navigation controller along with a table view controller.  Since the table view controller will display a table full screen, you don’t need to define a nib file for this view controller but instead configure everything through code, which isn’t as difficult as it sounds.  Let’s look at how to add a navigation controller plus table view controller to an empty application.

  • Start by creating a new X-Code project using the Empty Application template.

  • Create a new file (File > New > File…)
  • Make sure Cocoa Touch is highlighted in the iOS section on the left, select Objective-C Class and click Next.
  • Give it a name and make it a subclass of UITableViewController.  Make sure you un-select the With XIB for user interface check box before clicking Next again.

  • Save the file in your project folder.
  • Take a look at the header and implementation files for the new view controller and you’ll see that it’s already setup to display a table, what we need to do now is to load it and your navigation controller from the application delegate.
  • Open the appDelegate header file.
  • At the top, directly under the existing import command, add an import command for the root view controller’s header file. declare the navigation controller and define a property for it.  Your appDelegate.h file should now look like:

  • Switch to the appDelegate.m file.
  • Add the @synthesize command for the navigation controller property (remember to release it if not using ARC).
  • Find the – (BOOL)application:(UIApplication *) didFinishLaunchingWithOptions method which should look something like:

  • Remove the self.window.backgroundColor line and replace it with the following lines:
    • rootViewController *rootViewController = [[[rootViewController alloc] initWithStyle:UITableViewStylePlain]<autorelease>];
    • UINavigationController *aNavigationController = [[[UINavigationController alloc] initWithRootViewController:rootViewController]<autorelease>];
    • self.navigationController = aNavigationController;
    • [self.window addSubview:[self.navigationController view]];
  • Remember that autorelease is required for both the rootViewController and navigationController if you’re not using ARC.  The screen shot below is using ARC, whilst the example above is not.
  • The top part of your appDelegate.m file should now look like:

  • If you want to use the grouped table style, change the rootViewController initWithStyle to initWithStyle:UITableViewStyleGrouped.
  • Test your app by setting up an array in the root view controller and using that to populate the table, I’ve used the months array from my previous post.

Configuring The Detail View Controller

In this following example, I’m going to use the same detail view controller to display the detail info for each table cell, and that info is simply going to be the name of the month and number of days.  You can easily use a different view controller for specific table cells if you wish and I’ll give a quick example of this at the end.

  • Create a new file in your project for your detail view controller, make it a subclass of UIViewController and also create a nib file for simplicity.
  • Open up the XIB file in Interface Builder and add two labels, one to display the month name and another to display the number of days.
  • Open the detailViewController.h file and add properties for both the labels, along with a property to hold the month string that we’ll be passing from the rootViewController.  We’ll also define a dictionary to hold the month details we want to display:

  • Now switch to the implementation file and add the @synthesize commands to the top.

  • In vewDidLoad, setup the dictionary that holds the month details:

monthDetails = [[NSDictionary alloc] initWithObjectsAndKeys:@”31″, @”January”, @”28 (29 in a leap year)”, @”February”, @”31″, @”March”, @”30″, @”April”, @”31″, @”May”, @”30″, @”June”, @”31″, @”July”, @”31″, @”August”, @”30″, @”September”, @”31″, @”October”, @”30″, @”November”, @”31″, @”December”, nil];

  • Remember to release this dictionary in viewDidUnload and the properties in dealloc if you’re not using ARC in your project.
  • If it’s not already present, add the - (void) viewWillAppear:(BOOL)animated method as below:

  • In viewWillAppear, we set the month name label to display the selected month string which we’ll be passing from the root view controller and then set the number of days label to display the appropriate day string from the dictionary.
  • That’s almost it for the details view controller, all that’s left to do is switch back to the XIB file and set the outlets for our two labels to the appropriate properties.
  • Once done with that, switch to the root view controller implementation file and it’s time to add the detail view into the navigation controller.
  • First we need to import the detail view controller’s header file, so do this at the top above the @interface rootViewController () command and beneath the import for the rootViewController.h file.
  • Now find the - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method and take a look at the comments:
    • If we need to do anything for a specific cell, we do that.
    • We initialise a new detail view controller.
    • We pass the selected item to the detail view controller.
    • We push the detail view controller onto the navigation controller stack.

And that should be it, so give your app a twirl.  If you don’t want to use the same detail view controller for each cell, then all you do is set up a check for the indexPath.row (either in an if.. else or switch statement and pass the appropriate view controller using the same steps as above, here’s a sample from the settings view controller of FootPath:

 

 

Enable a GIT Repository for an Existing Project

I’ve only recently started using GIT to handle version management in my X-Code projects (I’ve just been using snap shots up until now), so have a number of projects that I needed to enable GIT version management for.  Here’s how to do it:

  • Close any open projects and close X-Code.
  • Open up the terminal in OS X.
  • Navigate to the project directory (say \Users\Craig\X-Code Projects\My Project).
  • Enter the following commands:
    • git init
    • git add .
    • git commit – m “initial commit (or what-ever comment you want to use)”
  • Start X-Code and load your project, X-Code should pick up your GIT repository automatically.

To check for a GIT repository in a particular project, open the project and then switch to the Organizer either by clicking the right toolbar button in the project window or selecting Organizer from the Window menu.  In the Organizer, click the Repositories icon and make sure your project is listed down the left hand side.  If it’s not, use the steps above to enable GIT for the project.

Once GIT is enabled, you should start to see the “A”, “M” etc icons along side the files in the Project Navigator and be able to commit changes from Source Control on the File menu (or by right / ctrl-clicking).

 

Core Data – Part 1: Introduction

So, I’ve been working on using Core Data to save route information in FootPath rather than NSKeyedArchiver which is what the app is currently using.  This is all down to performance reasons, since the more I use the app on my own iPhone 4 and the more routes I store, the longer it’s taking NSKeyedArchiver to save the data, and it’s now taking several seconds to store a new route or re-save the data after deleting one from the home screen.  This is quite obviously, pants, so I figure Core Data is the way to go and hopefully it’ll give me the performance improvements I need, plus it seems like a good opportunity to learn about it.  First, let me give you a high level overview of how FootPath works to give you an idea of the data I’m saving and how I’m using it.

The Problem

  • When the app starts, all the saved data is un-archived into a master NSMutableArray.
  • That array is scanned through and separate arrays for each week are generated, which are then used to build the table view.
  • The user taps the New button, the map screen is loaded which initialises a CLLocationManager object and starts getting location updates.
  • Once the horizontal accuracy for the generated location data has become a positive value, the app plots the users position on the map and displays the start button.
  • When the user taps the start button, a new route object is initialised and passed the current location object and date.  The route object stores these in it’s own NSMutableArrays.
  • Further location objects are passed every time location services updates it’s location, once a KM or mile has been reached, these locations are stored in additional arrays.
  • When the user exits the map screen and chooses to save, the route object is passed to the home screen’s view controller, which adds it to the master array and archives that array.
  • When the user taps on an entry in the table from the home page, the appropriate route object is loaded from the master array and passed to the detail screen’s view controller.
  • The detail screen view controller uses the location objects stored in the internal arrays of the route object to display the route on it’s map view.  The mile / KM arrays are used to position the split pins.

The underlying problem is that I’m storing a bunch of route objects (that in turn store a bunch of location objects) in an array and then need to archive the whole array of objects to the application’s Documents folder each time I save.  The more objects in the array, the longer it takes to save, so I need to look at alternatives.

Objectives

Well, the first thing I learned about Core Data is that it’s not the easiest of subjects to pick up for a relative iOS new-be like me, especially since I wanted to understand how to store several arrays of data rather than a simple object.  There seem to be endless examples on line, but pretty much all of them fell into one of two camps as far as I could tell:  They were either well documented but quite simple and only covered storing simple data structures or they covered more complex data structures but were pure code examples that lacked any decent (or any at all) documentation.  So I figure it’s worth while documenting my progress here in a series of posts as these may help others who are starting to look into using Core Data in an existing app, or at the least it’ll help me to better understand and remember what I’ve learned.

The first resource I really found useful was the Core Data Tutorial for iOS from Apple.  This falls into my well documented but simple example category, but at least it got me started and I recommend you work through it.  I don’t plan to just effectively repeat it’s content here since it already does a perfectly good job of describing what core data is, how to create the object model, entities, attributes and NSManagedObjects you need, along with how to load, delete and save data.  What I plan to do in this post is talk about how I decided on the design of my object model, and how I mapped my data to the entities and attributes available in core data.  In later posts I’ll expand on the example given in the apple document (another reason to work through it) to show how I determined how to store the information from my various arrays by creating additional entities and one to many relationships in core data.

Mapping My Data to Core Data

Once I’d worked through the apple document, I could setup and use core data in a simple example, but I still had a number of questions I needed to work out answers to, namely:

  1. Thinking of core data in terms of a database.  Each entity is a database table (and therefore record), each attribute is a field used to store specific data for that record.  I needed to decide how my data maps to this model, what database tables did I need to create and what fields did I need in each table.
  2. Each entity maps to an NSManagedObject (along with corresponding header and interface files), whilst each attribute maps to a property within the NSManagedObject.  I needed to understand how these NSManagedObjects would fit into my existing classes.  Does my route class need to be a subclass of NSManagedObject for example.
  3. Each item I want to save has additional arrays of data.  How do I save a single object in core data that has additional arrays of data?

To help answer the first question, I needed to consider the data that the app generates.  As already mentioned I’m currently storing arrays of objects (principally route, location and date) as can be seen by the init method for the route object:

- (id)initWithStartLocation:(CLLocation *)startLocation andStartTime:(NSDate *)startDate
{        
    if (self = [super init]) {
        self.routeCoordinates = [NSMutableArray arrayWithCapacity:1];
        self.mileCoordinates = [NSMutableArray arrayWithCapacity:1];
        self.mileTimes = [NSMutableArray arrayWithCapacity:1];
        self.kmCoordinates = [NSMutableArray arrayWithCapacity:1];
        self.kmTimes = [NSMutableArray arrayWithCapacity:1];
        self.totalDistance = [NSNumber numberWithDouble:0.0];
        
        [self.routeCoordinates addObject:startLocation];
        [self.mileTimes addObject:startDate];
        [self.kmTimes addObject:startDate];
    }  
    return self;
}

As you can see, I’m setting up a number of time and coordinate arrays for each route object I initialise and then use them to store CLLocation and NSDate objects.  However, if we look at the data types available in core data, it soon becomes apparent that short of encoding each array to and from NSData objects, I can’t just simply store these objects in core data:

I needed to think about what I wanted to save, why I wanted to save it and what format I needed to save it in.  Lets take a look at the home screen for FootPath and consider what data is being displayed here:

  • The title bar displays the total distance travelled across all routes.  This needs to be calculated each time the home screen is displayed as it’s obviously dependant on the routes displayed in the table.  Total distance is simply the sum of all route distances, so we need to make sure the route distance is stored as a numeric value and, since the distance is displayed in either miles or kilometers depending on the user settings, we’ll store the value for distance in meters so that we can convert it as needed.
  • The table section headers display the date for the week (I use Monday as the first day of the week).  This is generated when we first load the home screen and uses the date of each route, so we need to ensure we’re saving that but don’t need to save the generated week data itself as this will change as we add more routes and delete others.
  • The table cell displays the date and time of the route, along with the distance and time taken for the route.  We already have the date and distance covered so just need to consider time taken.  I’m not really doing anything with the time taken data either here or on the details screen, other than displaying it.
  • The table section footers display the total distance and calories used for all the routes in each week.  Along with distance, we therefore also need to be storing calorie usage for each route as a numerical value so that we can calculate this.

What can we determine from this?  Well, since we need to save, display and delete each route separately we need to define a route as an entity in our core data object model.  Also we know that each route entity will need the following attributes:

  • Distance (in meters) stored as a numeric value.  This is a double value within the app, so that’s what we’ll use in the object model.
  • Date and time for the route (core data allows us to store an NSDate object directly using the Date type).
  • Time taken for the route.  This could be stored as a numerical value if we needed to do something with it later but I’m using a string since I simply need to display it.
  • Calorie usage for each route stored as a numerical value.  This is a float value in the app, so we’ll use that data type in core data.

We need to also consider the requirements of the detail screen since this also displays route data:

  • Route distance and time, which we already have from the home screen.
  • Start location.  Currently, I’m storing the full CLLocation object as generated by core location in my routeCoordinates array, but I can’t do that in core data.  The Core Data Tutorial for iOS example shows how to store location data, get the longitude and latitude values and store each one as a separate double value in core data, so that’s what we’ll do.
  • Finish location.  See start location.
  • Split locations.  These are the coordinates for each mile or kilometer of the route, I’ll store them the same way I’m storing the start & finish coordinates, but I need to store several of them for each route.
  • The route.  This is the routeCoordinates array, with the first location object stored defining the start location and the last defining the finish.  Obviously there’s a ton of location data to save for each route so again it comes back to how I store an array of objects in core data.
  • Time for each split.  This is the time taken to travel a mile / kilometer or the final split to the finish.  Again it’s only being displayed, so it can be stored as a string, but there are potentially going to be several of them, one for each split location.
  • Calories used.  We have this in our list of attributes already.
  • Average speed.  Even though we only display this, we need to display it in either mph or km/h depending on settings, so we’ll store it as a numeric value to make conversion easier.

And there we have it, a pretty good map of the data we need to store and what attributes we need to create in our core data object model.  In reality, there’s a couple of additional strings I’m storing that are used to setup the weeks used by the table view and to ease displaying some other info, so the full object model for a route looks like the screen grab below:

That’s it for part one.  In the next part I’ll expand on the Apple tutorial to show how to add additional entities and relationships into your object model and how to save data to them.  Finally I’ll show how I’m storing my coordinate arrays in core data and how I go about migrating existing NSKeyedArchive data in the app across to core data at first launch.

Working With TableViews – Part 1

Introduction to using UITableView

Tables allow you to display lists in iOS and whilst you can use a table to simply show a list of values, they are mainly used for navigation as they are a perfect way to create a navigational hierarchy.  Rather than the traditional tables of a spreadsheet, UITableViews display a single column of cells, each cell representing a single object, be it a data value, object or choice for the user to make.  I use a UITableView in my FootPath app to to display the list of previous activities on the home screen, allowing the user to see a summary of everything they have recorded, navigate to the details screen for a particular activity and delete any they no longer want saved. I also use a one on the details page to simply display a list of split times with no interaction.

list of activities allows navigation and editing

Non-interactive list showing split times

There are two styles you can choose from when you implement a UITableView in your application, plain and grouped.  For an example of how a plain table looks, check out your contacts application which uses the plain style table view, whilst the settings app (and my FootPath app above) uses the grouped style.  Although the grouped style seems like it’s only for lists that have several sections, it can be used to display a single section, the split times table shown above uses the grouped style simply because I prefer the way it looks over the plain style.

In order to implement a UITableView, you need to supply two things: a data source and a delegate.  The data source is an object that defines the number of sections and rows in the table and manages the data model for inserting, deleting and re-ordering rows.  Typically the data source is the ViewController that displays the table, but it could be some other object that contains the data that you wish the table to display.  Whatever object you use for the data source, it must conform the the UITableViewDataSource protocol and implement at least the following two required methods:

  • tableView:cellForRowAtIndexPath:
  • tableView:numberOfRowsInSection:

The delegate object manages what happens when a user selects or edits a cell along with settings to define things like the height and view used for the section headers and footers or the height and indentation of a row.  It can be same same or a different object to that used for the data source and needs to conform to the UITableViewDelegate protocol, although there are no required methods for this protocol, if you want the user to be able to make selections, you will need to implement the following method:

  • tableView:didSelectRowAtIndexPath:

You’ll notice that many of the table view methods use indexPath to identify specific sections and rows within the table.  UITableView declares a category on NSIndexPath to add two properties to allow you to specify a section (indexPath.section) and row (indexPath.row) in your table.

OK, so lets set up a really simple table using Interface Builder.  Start by creating a Single View based iPhone application in Xcode – I’ve chosen not to use story boards or ARC for mine – and once it’s been created, open the view controller’s interface file (<view controller.h>).  Add the following to the end of the @interface line: <UITableViewDataSource, UITableViewDelegate> { and press return a couple of times.  The corresponding } brace should be added automatically and between them you want to add the line: NSArray *monthArray; to define an array that we will use to hold our sample data.  Your interface should now look something like this:

@interface myViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {

NSArray *monthArray;

}

Close the interface file and open the implementation file (<view controller.m>).  You may notice that Xcode displays a number of warnings at this point, this is because X-Code is aware that you haven’t implemented the required methods for the protocols yet, we’ll get to those shortly.  First thing we need to do is to setup the array that holds the data that our table is going to display, so in the [viewDidLoad] method, add the following line after [super viewDidLoad];

monthArray = [[NSArray alloc] initWithObjects:@”January”,@”February”,@”March”,@”April”,@”May”,@”June”,@”July”,@”August”,@”September”,@”October”,@”November”,@”December”,nil];

Next, create a Dealloc method to free up the memory used by the array we’ve just created:

- (void)dealloc
{
[monthArray release];
[super dealloc];
}

Now we can get to defining the methods required by the protocols, plus we’ll also define one additional optional method.  The first one we’ll define is the optional numberOfSectionsInTableView: method which simply needs to return an NSInteger defining the number of sections for the table.  We could calculate this from our data set, but in this case we know that we only want a single section, so we’ll just simply return the value of one.  This method is optional since the default value used is one anyway, but we’ll still define the method now since we’ll use it later (and you’ll obviously need to us it when your table needs more than one section).  So add the following to your implementation file:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

Next up we’ll specify the number of rows per section by implementing the tableView:numberOfRowsInSection: method.  Again, for this example things are pretty straightforward since we only have one section and so only need to return one value, but for more complex data sets we’ll need to decide how to return the number of rows for a given section index.  Since our data set is simply the months of the year, we know the value that we need to return is twelve so we could just go ahead and do that like before, but what if we were generating the array from some external data source, or we had a mutable array that we couldn’t guarantee had a fixed number of entries?  Well, for arrays it’s simple, we just use the count of the array as the return value, so go ahead and add the following to your implementation file:

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

Finally we’ll define the method that does the bulk of the work in generating and displaying the cells themselves, tableView:cellForRowAtIndexPath:  This method returns a UITableViewCell object for the table to display at a given row, with the initialisation of the cell object being done by some pre-defined code that has already been setup by Apple.  What this code does is to re-use cells as they disappear off the screen, due to the user scrolling, rather than to release and reallocate memory for each one.  Google will bring back many detailed descriptions if you want more information on what the code actually does, but needless to say you want to add the following code into you implementation file:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (nil == cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@”cell”] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}

However, that code on it’s own isn’t enough, for a start we aren’t returning anything, and we need to return the UITableViewCell object, we also need to configure the cell, here by setting it’s title property, so before the final brace, insert the following:

cell.textLable.text = [monthArray objectAtIndex:indexPath.row];
return cell;

What we are doing here is simply setting the textlable.text property for the cell to an object in our data array, since our array holds string objects.  We use the indexPath.row property to tell us which row we require data for and grab the object that’s in that position of the array since both the table’s and arrays indexes start at 0.  So for row one of the table (indexPath.row=0) we’ll grab January, for row two we’ll grab February, row three will be March and so on.  Once you’ve added the three methods, you should see the warnings in Xcode disappear since we have now implemented the methods required by the protocols.

The final thing we need to do for this example is to add a table view to our view controller, so open up the nib file (<view controller.xib>) and from the object library drag a tableView object onto the view window so that it takes up the entire screen.  Now with the newly added table view selected, switch to the connections inspector on the right and drag from both dataSource and delegate under Outlets across to the File’s Owner object in the dock.  This tells the table view to use the view controller for it’s delegate and data source.  Lastly, select the View object in the dock and in the Connections Inspector, under Connecting Outlets, drag from New Outlet over to File’s Owner and select View.  This last step needs to be done for the Single View Template to work, otherwise it throws an exception when the app starts.

OK, one last thing – we’ve just created our table view manually, but there’s a quicker way.  In your project create a new file, select the Objective-C class template and make it a sub class of UITableViewController.  This will give you a nib file with the table view object already present and an implementation file with the required methods already defined.  All you have to do is customise those methods to return the values you need.

That’s it for this part.  In later parts, I’ll cover adding a table view via code, using table views with navigation controllers and customising how the table view looks.

Useful Links:

UITableView Class Reference

UITableViewDataSource Protocol Reference

UITableViewDelegate Protocol Reference

TableView programming Guide for iOS

Customising Action Sheets

I wanted to customise the button order in an action view that I was creating for one of my apps, so I thought I’d document the process.  First lets look at the normal way of initializing a new UIActionSheet:

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@”Action Sheet” delegate:self cancelButtonTitle:@”Cancel” destructiveButtonTitle:@”Destructive”   otherButtonTitles:@”Button 1″, @”Button 2″, nil];
[actionSheet showInView:self.view];
[actionSheet release]

Which produces the following action sheet:

The destructive button is at index 0 (top), cancel is at the bottom (index 3 in this case) and any other buttons are in between.  Which is fine if I wanted the standard layout, but I wanted the buttons to be in a different order, so I took a look at the UIActionSheet class reference document and noticed two properties that looked like they would do the trick, namely cancelButtonIndex and destructiveButtonIndex.  The properties are not read only, which means we can set them, so let’s give them a whirl by adding the following two lines to the above code before we call showInView:

actionSheet.cancelButtonIndex = 1;
actionSheet.destructiveButtonIndex = 2;

This makes the action sheet look like:

Now, I should point out that if you look at the discussion section for the relevant property reference, you’ll come across a statement like the one below which is from the cancelButtonAtIndex reference:

“Button indices start at 0. The default value of this property is normally -1, which indicates that no cancel button has been set. However, a cancel button may be created and set automatically by the initWithTitle:delegate:cancelButtonTitle:destructiveButtonTitle:otherButtonTitles: method. If you use that method to create a cancel button, you should not change the value of this property.”

I think we can see from the above example why the button index properties shouldn’t be set when usinginitWithTitle:delegate:... Although it will technically work – all we are really doing by setting a button to either cancel or destructive using the properties is changing it’s colour to black or red respectively.  There’s nothing else that distinguishes those buttons from the other two we’ve defined, and in the action view’s delegate method we use the passed buttonIndex value to determine which action to take.  What it does do is make out code confusing and difficult to follow – the destructive and cancel buttons defines in the init method are not necessarily the destructive and cancel buttons the user will see.  So, what’s the correct way of changing the button order in an action sheet?

Well, it’s to initialise simply with init and then to use the addButtonWithTitle: method to add each individual button in the order you want them listed like so:

UIActionSheet *actionSheet = [[UIActionSheet alloc] init];
actionSheet.delegate = self;
actionSheet.title = @”Action Sheet”;
[actionSheet addButtonWithTitle:@"Button 1"];
[actionSheet addButtonWithTitle:@"Button 2"];
[actionSheet addButtonWithTitle:@"Destructive"];
[actionSheet addButtonWithTitle:@"Cancel"];
[actionSheet showInView:self.view];
[actionSheet release];

The order we add the buttons in the method above is the order that they will be displayed in the action view, with the first button at index 0 and so on:

Hmm, that’s better – the buttons are in the order we want, but what happened to displaying the destructive button in red and the cancel button in black?  Well, we haven’t specified which of our buttons are the destructive or cancel ones, that’s what the two properties we tried earlier are for.  So let’s add the appropriate lines to our code, bearing in mind that our destructive button is at index 2 and our cancel button is index 3:

actionSheet.destructiveButtonIndex = 2;
actionSheet.cancelButtonIndex = 3;

Which gives us exactly the result we want, but we’re not quite done yet.  Take a look at the addButtonWithTitle: reference and you’ll see that it returns an NSInteger which just happens to be the button index, so we can combine our property and add button calls into a single line, like so:

actionSheet.destructiveButtonIndex = [actionSheet addButtonWithTitle:@"Destructive"];
actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:@"Cancel"];

So that’s it, you can now create action sheets with custom button ordering.  There’s one more property that’s worth having a play with, and that’s actionSheetStyle.  this lets us define the look of the action sheet using one of the defined constants:

  • UIActionSheetStyleAutomatic
  • UIActionSheetStyleDefault
  • UIActionSheetStyleBlackTranslucent
  • UIActionSheetStyleBlackOpaque

Automatic will take on the style of the bottom bar, otherwise it will look the same as default (shown in the examples above).  Translucent will show whatever is behind the action sheet whilst opaque is solid black as below.

Hope you find this useful!

Add a viewController to the Empty Application Template in X-Code 4.2.1

Following on from me wanting to recreate the window based application template in X-Code 4.2.1, I decided to reverse engineer adding a view controller to the blank application template.  Might be useful for some (and me when I loose my notes!)

  1. From your empty application, create a new UIViewController and XIB file.  From now on, I’ll refer to this view controller simply as ViewController, and you should substitute your view controller’s name instead of this.  For example, if you called your view controller “MyViewController”, on the next line you should use the command: #import “MyViewController.h”.
  2. Open the application delegate header file and add the #import “ViewController.h”; line.
  3. Open the application delegate implementation file and add the line ViewContoller *viewController;
  4. in the -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method, directly above the line [self.window makeKeyAndVisible]; add the following to additional lines:
  • viewController = [[[ViewController alloc] initWithNibName:@”<xibname>” bundle:nil] autorelease];
  • self.window.rootViewController = viewController;
  1. Finally add [viewController release]; to the dealloc method (creating the method if it doesn’t exist).

My First App Store Submission

I’ve been working on a personal app project for the last month or so, and today I decided that I’ve really done enough code tweeking and really should get on with submitting it the app store.  So that’s what I’ve done this morning, it’s my first app submission (but hopefully not my last), and I’ve got to say, it’s not the most obvious process in the world, so I’ll probably create a blog post detailing the steps so I can remember for next time!

For details of my app, look here.

Creating an iPhone Window-based Application in X-Code 4.2.1

About three weeks into learning Objective-C, I installed X-Code 4, and this is where I hit one of my first hurdles.  A lot of the books and Internet resources I was using at the time based their exercises on the Window-based application template available in X-Code 3, and missing from version 4.  Looking at the available templates in X-Code 4, it looked like both the blank application and single view templates where close to what I needed, but neither exactly matched the window based template that the samples used.  Time to reverse engineer my own window based app in X-Code 4.

  1. Create a new project using the empty application template.  I didn’t want to use storyboards or automatic reference counting, so I left those options unchecked.
  2. Once you have your blank application, on the File menu, select New > New File.
  3. On the template selection screen, highlight User Interface under iOS, select the Window icon and click Next.
  4. Select the platform (iPhone for me) and name the file MainWindow.
  5. Move your new MainWindow.xib file to the appropriate group in the project navigator window, and with the file selected, click on File’s Owner in the Placeholders section of the dock to the right.Set Files Owner class to UIApplication
  6. Use the Identity Inspector to change the class to UIApplication.
  7. Now, look in the library and drag an Object into the Objects section of the dock above Window.Add an Object to the dock
  8. Using the Identity Inspector, change the class of the Object to the Application Delegate class for your project (take a look at the name in the project navigator pane).  This will also rename it in the dock to match.Change th class to match your app delegate
  9. Open your application delegate header file
  10. Locate the @property line for the UIWindow property and modify it to read @property (strong, nonatomic) IBOutlet UIWindow *window;
  11. Now open MainWindow.xib again.
  12. Right click on File’s Owner and drag from delegate under Outlets to the application delegate object in the dock you added earlier.
  13. Now right click the application delegate object and drag from window under Outlets to the Window object in the dock.Connect the window outlet to the window object
  14. Click your project name at the top of the project navigator to display the project summary page.
  15. In the Main Interface drop down, select MainWindow.
  16. Finally open your application delegate implementation file and in the didFinishLoadingWithOptions method, remove all lines except [self.window makeKeyAndVisible]; and return YES;.

You’re done, you should now have a useable copy of a window based application template in X-Code 4.

Follow

Get every new post delivered to your Inbox.