UINavigationController with MonoTouch - Building a simple RSS reader - Part 2

Written on

This is the second part of a two-part tutorial showing you how to create a simple RSS Reader application for the iPhone using C# and MonoTouch. The application will use a UINavigationController to navigate back and forth between a news list (in a UITableView) and a second view showing more details about the news article that the user selects.

In this part of the tutorial we are going to finish this app by doing the following:

  • Create a second view, which will display more information about the news article.
  • Navigate forward to that view when the user selects an item

The first thing we need to do is to create a new view. We want to show this view when the user selects a news story from the list, where we will show more information about that news story. Click File -> New File and select the "View Interface Definition with Controller" option. Name the view "DetailsViewController":

Double-click the .xib file and go into Interface Builder. Drag a UILabel onto the view, at the top. This will be for the headline, so make the font bold, a bit bigger, and allow the number of lines to be 2 or 3 (the default is 1). Then drag a UITextView onto the view, below the UILabel you just created.

Next, we need to create two outlets for these UI components we just created - it will be these outlets that we can work with in the C# code. This is done in the same way as the outlet for the UINavigationController was created in the first part of this tutorial. I have named the UILabel "headlineLabel" and the UITextView "contentTextView". When you are finished it should look like this (note, you can see the outlets are connected correctly to the UI components in the "outlets" section, at the top-right of this picture):

Save and quit Interface Builder, we are finished with it for this tutorial.

We are not quite finished with MyDetailsViewController. Open up the MyDetailsViewController.xib.cs file. We want to load this view from the XIB file when the constructor is called. We can do this by calling a base constructor, passing in the XIB file name:

public DetailsViewController() : base("DetailsViewController", null)
{
    Initialize();
}

We also want to have a public instance property of a NewsArticle in this MyDetailsViewController class, and bind the NewsArticle to the labels we created. The actual binding occurs in the ViewWillLoad method. This event gets raised just before the view loads, as the name suggests.

public NewsArticle CurrentNewsArticle;

public override void ViewWillAppear(bool animated)
{
    base.ViewWillAppear(animated);
    BindItem();
}

private void BindItem()
{
    headlineLabel.Text = CurrentNewsArticle.Headline;
    contentTextView.Text = CurrentNewsArticle.Content;
}

In the MyTableViewController.cs file, there is a class named TableDelegate which has a method called RowSelected. As the name of the method describes, this method gets called each time a row is selected in our table. At the moment we are just calling Console.WriteLine here, but instead, now we want to create an instance of our DetailsViewController and display it.

DetailsViewController detailsViewController;

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    if (detailsViewController == null)
        detailsViewController = new DetailsViewController();
				
    NewsArticle newsArticle = tvc.NewsArticles.ElementAt(indexPath.Row);
    detailsViewController.CurrentNewsArticle = newsArticle;

    tvc.NavigationController.PushViewController(detailsViewController, true);
}

Notice that I declare the detailsViewController variable on the class itself, this way I only actually instantiate it once and can reuse it each time a use clicks on a row - which will aid performance.

I fetch the NewsArticle object from the list at the row that the user clicked, then set the NewsArticle property we created earlier.

The interesting line of code is the last one: it is PushViewController that actually navigates us forward to a new view. Notice that the second parameter "true" is saying that we want this navigation process to be animated.

Once this is done we are almost there. There are only two more things that are missing: the first table view has the title "item" because we never bothered to change it when we created the table view. We can set the title in the constructor of the MyTableViewController class like this:

public MyTableViewController (IntPtr p) : base (p)
{
    Title = "Tottenham News";
}

We also want the standard arrow to the right of the cell, so it looks like it is clickable. We can do this directly below the line of code where we create the UITableViewCell:

cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;

Finally we are ready to build and run this application. The end result is shown below:

RSS Reader - Final result

I hope you found this tutorial helpful. Any questions or feedback are appreciated in the comments.


Comments

MonoTouch Article - UINavigationController with MonoTouch - Building a simple RSS reader - Part 2 Thank you for submitting this entry - Trackback from MonoTouch.Info

MonoTouch.Info

UINavigationController with MonoTouch - Building a simple RSS reader - Part 1

Alex York .NET

Great tutorial, thanks. Any idea why I get this exception? Unhandled Exception: MonoTouch.Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInvalidArgumentException Reason: *** -[UILabel copyWithZone:]: unrecognized selector sent to instance 0x49ebf50 at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:void_objc_msgSend_IntPtr_Boolean (intptr,intptr,intptr,bool) at MonoTouch.UIKit.UINavigationController.PushViewController (MonoTouch.UIKit.UIViewController viewController, Boolean animated) [0x00000] at iTecTeacher.EpisodesTableViewController+TableDelegate.RowSelected (MonoTouch.UIKit.UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath) [0x00000] at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr) Thanks.

Christian Weyer

Seems I had a custom outlet named 'title' - doh...

Christian Weyer

@Christian: glad you liked the post - sounds like you also fixed the problem you were having :-)

Alex

Excellent tutorial! I just have one problem to fix. In the RowSelected method, this line causes an exception: tvc.NavigationController.PushViewController(detailsViewController, true); The reason it fails is that the NavigationController property is null. Examining this property at various stages during the life cycle of MyTableViewController - it is always null. There is probably some tiny snippet of code that I've missed... can you help?!

Hugh Mullally

@Hugh: zip up your project and email it to alex-york at hotmail dot co dot uk if you want me to take a look at it. Also, I have included my zipped-up source code at the top of the post, you could try and compare it to yours for any differences.

Alex

@Hugh: you assigned the "MyTableViewController" class to the UINavigationController in Interface Builder, whereas it should have been assigned to the UITableViewController :-) A video reply is here: http://screenr.com/x9B

Alex

@Alex : Thank you very much for that - the screencast was excellent! Everything works now - just need to change the RSS feed to pick up the Liverpool feed now ;-)

Hugh Mullally

Great post.

waggi

Great tutorial !! it really helps to get me started on iPhone development. I have a question though: What if i have more pages on the TableViewController. For instance, i would have 4 TableView pages before jumping to a Detail Screen. I could create a TableView for everyscreen, but i guess there should be a better way of re-using just one tableView. Will the animation still slide to a next page? Should i swap between 2 TableViews? Thanks. Tony

Tony Tromp

@Tony: sorry for the late reply :-) When a row is selected in your UITableView, you are simply navigating to another UIView. That view could also contain a UITableView too, which once again, could navigate to a 3rd View containing a table. I don't think that these views, if they genuinely are different views, can or should be shared i.e. have one UITableView for your whole app. You might be able to work it like that, but I would be tempted to have 2 separate Views. Each View can be reused though: so the second time a user hits a View, it would be the same instance as the first visit, but maybe displaying different data. Make sense?

Alex

Hi, Thanks for a great tutorial! I'm having one problem though, I'm not able to set the title for the TableView, but I can set it in the DetailedView. public MyTableViewController (IntPtr p) : base (p) { this.Title = "Tottenham Hotspurs"; Console.WriteLine("test"); } The console writes "test" as it should but the "Title" title is till there. Any thoughts what I'm doing wrong? Thanks! /Andreas

Andreas Björkblom

@Andreas: If it's running the Console.WriteLine code, the Title would seem to be overwritten somewhere later on in the lifecycle of the TableViewController. Off the top of my head you could try and set the Title in the ViewDidLoad method instead (override it). Also, I may have set the Title in Interface Builder - so check there too and alter/remove it if necessary. I will update this post later when I get home and have checked it out more.

Alex

The UI designer now puts the assignment of Title in the ViewDidLoad which overwrites the assignment in the constructor

pauliom

Sorry I meant the default template <oops>

pauliom

The first mention of MyDetailsViewController is "We are not quite finished with MyDetailsViewController." - I'm guessing this is really a new class, not an error and it should be MyDetailsViewController? Also, as I'm so new, it took me about 10 mins to work out to use the Files owner for the outlets.

Stu

Ooop, sneaked a peak into the zip - and it is meant to be in DetailsViewController.xib.cs

Stu