Saturday, February 27, 2010

[iPhone] Combining Nav Bar and Tab Bars

After watching the Lecture #7 of the iPhone Programming course from Stanford University (which you can found here), the implementation of navigation bars was quite clear to me. However I had a bit of difficulties to understand how to combine table bars and navigation bars. Luckily enough I found this screen cast from OReilly's Elisabeth Robson in Youtube:



In order to get the general process of building such an application I am writing here some conceptuals steps to follow when combining Tab bars and navigation bars. I hope it helps you as well.

  1. Make new iPhone project in XCode (Window based application)
  2. Create the rootController as a UITabBarController (not as a UINavigationController since the tab bar will be the main tool you will use to move through the application). This root controller will go into the Application delegate as an IBOutlet.
  3. The Tab bar will be the first view, therefore the view has to be added in the "applicationDidFinishLaunching" method.
  4. Open the Interface Builder for the Main Window and drag a Tab bar controller into the window so you can hook it up to the outlet you created in the AppDelegate.
  5. For the navigation bar, an associated controller has to be created (it must he a subclass of UINavigationController). Note that the navigation bar has its own view to which the different views will be stacked.
  6. Also an IBOutlet for the navigation controller must be created in the application delegate (don't forget to reference the class with the @class directive).
  7. This tutorial features a table besides the tab bar and navigation bar, therefore we need to create a controller for the table view (so we can manage the table view).
  8. In the Table view controller do not forget to create the outlet to the table view that the table view controller will manage.
  9. Create a new NIB for the table view and associate its File Owner to the table view controller.
  10. In this example, the table view controller is also the delegate and datasource of the Table and as such it will contain an array that will hold the data (the array is created in the viewDidLoad method of the Table view controller).
  11. Set the title of the Table View Controller. This title is the one that will appear in the Navigation bar. Note, the screen cast uses NSLocalizedString to set the title. This class is useful for generating strings that will have to change depending on the language.
  12. In the numberOfRowsInSection, we must set up the size of our array. Also set the content of the row at indexPath in the cellForRowAtIndexPath. Note that the screencast uses the cell.text field to set up the field. In iPhone OS 3.0 this is deprecated. You should use cell.textLabel.text property instead. So, in the tutorial, instead of using
    cell.text =  [bookArray objectAtIndex:row] ;
    use
    cell.textLabel.text = [bookArray objectAtIndex:row] ;

  13. We need to create a simple view that will display the details of each book. So proceed to create a UIViewController class with the associated NIB and do the corresponding class and view hooks.
  14. Then we must inform the TableViewController about this View (since the Table View controller will push and pop the detail view controller, it must have a reference).
  15. In the definition file of the TableViewController do not forget to import the detail view controller AND the Application Delegate since we will need to have access to the navigation bar controller (in order to push and pop views)
  16. The push of the detail view controller will be done in the didSelectRowAtIndexPath method. In this tutorial the navigation controller that pushes the view is done via calling the delegate of the AppDelegate and then accessing the navigation controller. Alternatively, from the Stanford tutorial, it seems that the TableViewController can access the navigation Controller using the following line.
    [self.navigationController pushViewController:bookDetailViewController animated:YES];

Let me know if you have comments on this post.

Enjoy!

Sunday, February 21, 2010

How to use XML Builder templates in Rails (I)

For an application I had to generate a customized XML view in a Rails application. As it is known, Rails comes with the Builder gem that is a tool to render XML files from templates. Documentation of this gem can be found in Rubyforge or in Ruby API description.

In order to customize this xml, I used as a starting point this stackoverflow post. For this purpose I have created a simple Rails application using scaffolding (employees) with a single database table that is a list of employees with the following 4 fields: age, category, family name and given name). In order to customize the XML when the user types http://localhost:3000/employees/1.xml (for instance), do the following:
  1. Create a builder template that is called, for instance, show.builder, this template is the one that will be used to show the database entry (in my sample application it would be (http://0.0.0.0:3000/employees/1.xml). The code of this template is the following (tags are in spanish):

    xml.instruct!

    xml.employee do

    xml.nombre(@employee.given_name)
    xml.apellido(@employee.family_name)
    xml.edad(@employee.age)
    xml.categoria(@employee.category)

    end

  2. In the controller (in my case employee_controller) I had to substitute the line

    format.xml { render :xml => @employee }

    by the line

    format.xml { @employee }

    Otherwise the application will render the XML in the default mode. If you still want to keep the default XML format, you can always redirect the rendering using the routes.cb configuration file.
If in addition you want to generate the XML and send to the user (besides showing it in the browser, change the previous format line:

format.xml { @employee }

by

format.xml do
stream = render_to_string(:template=>"employees/show.builder" )
send_data(stream, :type=>"text/xml",:filename => "test.xml")
end


This way, the Rails application renders the template and places it in stream and afterwards send this data into the test.xml file.

That's it!