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

Written on

This is the first 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:

  • Create a UINavigationController, with a UITableViewController inside it.
  • Show a list of News Articles in the UITableView, which are taken from an RSS Feed

Firstly, create a new solution in MonoTouch called RssReader.

Create a new solution

Double-click "MainWindow.xib" which should launch Interface Builder. Our first task is to find a UINavigationController in the Library, and drag it onto the window with the title "MainWindow.xib". I recommend using the tree view mode in this window, not the icon view. After you drop your UINavigationController in there, it should look like this:

UINavigationController in your project

Once that is done, we need to create an outlet in Interface Builder, so that we can work with our UINavigationController in our C# code. To do this differs slightly depending on which version of Interface Builder you have installed, but in the latest version (at the time of writing) this is done in the Library window (it used to be done in the Inspector window). In the Library window, I need to select "Classes" at the top, then select "AppDelegate" in the list, and finally select "Outlets" at the bottom. By clicking the + button we can add a new outlet. I'm going to name it "navigationController", and once you're done you should have something looking like this:

Create an outlet for the UINavigationController

After creating the outlet, it's not actually connected to anything yet - so now we need to connect it to our UINavigationController. In the Inspector window, on the Connections tab, you should see your outlet, and an empty circle next to it, meaning it's not connected to anything yet. Drag that circle to your UINavigationController to make the connection.

Connect the outlet to the UINavigationController

Note that I'm dragging from right to left in the diagram above: connecting the outlet to the UIViewController (NOTE: I originally forgot this step in the first version of this tutorial - thanks for pointing it out Bruce).

After this is done, I'm going to find an item called UITableViewController. This is where we will display news articles that we have taken from an RSS feed. Drag it directly onto the Navigation Controller that we created earlier. You can drag it into the navigation controller in the "MainWindow.xib", or do it directly onto the designer. Once you're done with that, you should have something that looks like this:

This time, we are not going to create an outlet directly, but instead we are going to give it a class name. We haven't created this class yet, but we will do shortly. Type "MyTableViewController" into the class identity, like this:

Assign a class to the navigation controller

Remember, we haven't created this class yet, but that is our next task. For now, we are finished with Interface Builder. Save, and quit.

Okay, so now we should be back in MonoDevelop, ready to write some code. Create a C# class called "MyTableViewController.cs". This is the class that we have already told the UITableViewController to look for. I suggest you go to the Code Snack blog post on UITabBarController and add the template for a UITableViewController. Once the template is added, if you delete all of the code in a new class and type "uitvc" and click Tab on your keyboard, you end up with a standard template for a UITableViewController:

using System;
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace RssReader
{
// [MonoTouch.Foundation.Register("MyTableViewController")]
public partial class MyTableViewController : UITableViewController
{
static NSString CellID = new NSString ("MyIdentifier");

// Constructor invoked from the NIB loader
public MyTableViewController (IntPtr p) : base (p)
{
}

// The data source for our TableView
class DataSource : UITableViewDataSource
{
MyTableViewController tvc;

public DataSource (MyTableViewController tableViewController)
{
this.tvc = tableViewController;
}

public override int RowsInSection (UITableView tableView, int section)
{
return 1;
}

public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell (CellID);
if (cell == null)
{
cell = new UITableViewCell (UITableViewCellStyle.Default, CellID);
}

// Customize the cell here...

return cell;
}
}

// This class receives notifications that happen on the UITableView
class TableDelegate : UITableViewDelegate
{
MyTableViewController tvc;

public TableDelegate (MyTableViewController tableViewController)
{
this.tvc = tableViewController;
}

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
Console.WriteLine ("Row selected {0}", indexPath.Row);
}
}

public override void ViewDidLoad ()
{
base.ViewDidLoad ();

TableView.Delegate = new TableDelegate (this);
TableView.DataSource = new DataSource (this);
}
}
}

It's very useful to get all of that code for free. The key things to note here are:

  • There is a nested class called DataSource. This class has two important methods called RowsInSection and GetCell. We will use both of these later.
  • There is another nested class called TableDelegate. This handles things such as when the user selects a row (the RowSelected method). Later, we will navigate to another view and show details on the story that the user selected.
  • Finally, there is a method called ViewDidLoad. This will be useful, because it will be here that we will fetch the news stories from an RSS feed.

NOTE: at this point, check and see if your MainWindow.xib.designer.cs file has a partial class definition for MyTableViewController, decorated with a Register attribute. Mine did (it was generated automatically) but for some readers this didn't happen (see the comments). If not, simply decorate your MyTableViewController class with the Register attribute (it is commented out in the sample above.

If you run this app right now, it should build but you will see nothing, just an empty screen, not even our UINavigationController. That's because we haven't told it to load the navigation controller yet! If you remember, we created an outlet called "navigationController" at the start, in Interface Builder. If you go to the Main.cs file in MonoDevelop, you will see a method called FinishedLaunching. It is here that any startup views need to be added. There might already be a line commented out that does what we want. Either way, make sure we now have this code in the FinishedLaunching method:

// This method is invoked when the application has loaded its UI and its ready to run
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window.AddSubview(navigationController.View);

window.MakeKeyAndVisible ();
return true;
}

At this point your app should run and look like this:

The app, as viewed in the simulator

This what we should expect. We can see the navigation at the top, and we can see the table in the middle, the only thing we lack is the data. Let's do that now. Firstly, create a new C# class called NewsArticle.cs like this:

using System;

namespace RssReader
{
public class NewsArticle
{
public string Headline { get; set; }
public string Content { get; set; }
}
}

Let's head back over to our MyTableViewControllerClass and get the real data and bind that to the table. Create a generic List<NewsArticle> right at the top of the class:

public partial class MyTableViewController : UITableViewController
{
List<NewsArticle> NewsArticles;
...

We will fill this list with the real data in the ViewDidLoad method. I am going to use LINQ and the System.Xml.Linq namespace here to populate the List of NewsArticles. As you can see, I have hardcoded the RSS Feed URL. I am using the BBC Tottenham Hotspur feed because that's the news I'm interested in, but you can use anything you want:

public override void ViewDidLoad ()
{
base.ViewDidLoad ();

XDocument rss =
XDocument.Load("http://newsrss.bbc.co.uk/rss/sportonline_world_edition/football/teams/t/tottenham_hotspur/rss.xml");

var query = from item in rss.Descendants("item")
select new NewsArticle
{
Headline = (string) item.Element("title"),
Content = (string) item.Element("description")
};

NewsArticles = query.ToList();

TableView.Delegate = new TableDelegate (this);
TableView.DataSource = new DataSource (this);
}

XDocument is in the System.Xml.Linq namespace - you will need to right-click of References in your project and add it. Then you will need to add a using directive at the top of your code file to that namespace, and also to System.Linq.

using System.Linq;
using System.Xml.Linq;

The RowsInSection method is currently hardcoded to "return 1" but we want it to return the dynamic length of our news article list. Let's change it to this:

public override int RowsInSection (UITableView tableView, int section)
{
return tvc.NewsArticles.Count;
}

And one final adjustment is to set the text of each cell in the table. We already have a comment that says this:

// Customize the cell here...

Change it to this:

// Customize the cell here...
NewsArticle article = tvc.NewsArticles.ElementAt(indexPath.Row);
cell.TextLabel.Text = article.Headline;

We are now finished with part 1. You can see the result here:

The final result

You can actually see slighly more than just the simulator in that last screenshot. You can also see the Application Output window. It shows that we are using Console.WriteLine to print which row was clicked when the user taps a row. This will become important in the next part of this tutorial, when instead of doing a Console.WriteLine, we will use the NavigationController to navigate forward to a new view, where the user can read more details about the news article that they selected.

Hope you liked part 1 of this tutorial and found it helpful. Head on over to part two and finish the app by navigating to a second view.

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


Comments

Alex York .NET | UINavigationController with MonoTouch - Building a simple RSS reader - Part 1 Thank you for submitting this entry - Trackback from MonoTouch.Info

MonoTouch.Info

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

Alex York .NET

I was following your example and got as far as creating my class called "MyTableViewController.cs". When I tried to Run on the Simulator, I receive the following error: 2009-09-20 20:43:48.928 TestUINavController[25455:40b] *** -[MyTableViewController setFrame:]: unrecognized selector sent to instance 0x3e22520 Unhandled Exception: MonoTouch.Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInvalidArgumentException Reason: *** -[MyTableViewController setFrame:]: unrecognized selector sent to instance 0x3e22520 at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr) at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00000] at MonoTouch.UIKit.UIApplication.Main (System.String[] args) [0x00000] at TestUINavController.Application.Main (System.String[] args) [0x00000] in /Users/owner1/Desktop/iPhoneDevelopment/TestUINavController/TestUINavController/Main.cs:14 Any ideas as to why?

Quan

Hmmm, I think you forgot to say connect the AppDelegate to the Navigation Controller otherwise you don't get this in the code behind- // Base type probably should be MonoTouch.Foundation.NSObject or subclass [MonoTouch.Foundation.Register("AppDelegate")] public partial class AppDelegate { [MonoTouch.Foundation.Connect("window")] private MonoTouch.UIKit.UIWindow window { get { return ((MonoTouch.UIKit.UIWindow)(this.GetNativeField("window"))); } set { this.SetNativeField("window", value); } } [MonoTouch.Foundation.Connect("navigationController")] private MonoTouch.UIKit.UINavigationController navigationController { get { return ((MonoTouch.UIKit.UINavigationController)(this.GetNativeField("navigationController"))); } set { this.SetNativeField("navigationController", value); } } }

Bruce Mcleod

I figured this out. Error on my part. I clicked on the UITableViewController before naming the class in Identity. I removed the controller, re-added, and changed the class name right away instead of clicking on anything in Interface Builder.

Quan

@Quan: glad you fixed the problem - sounds like you had given the wrong thing the "MyTableViewController" class name. @Bruce: Thanks for pointing that out - I have updated the tutorial and added in that step.

Alex

I forgot to say THANKS for this sample! I just asked someone on the Monotouch mailing list for possible code examples and then found this page. This has been a great help...Look forward to trying to implement the 2nd tutorial tonight...

Quan

This might be localised weirdness but I had to include two Linq references to MyTableViewController.cs and add an extra reference of System.Xml.Linq before XDocument would recognise. using System.Linq; using System.Xml.Linq; The error was "The type or namespace name 'XDocument' could not be found. Are you missing a using directive or missing an assembly reference" The up side is this is just so great to have .NET to play with on the iPhone.

Bruce Mcleod

@Bruce: it's not localised weirdness, XDocument is in the System.Xml.Linq assembly which is not added as a reference by default in a MonoTouch project - so you will need to do it manually. Then you need to add the "using" directives for that namespace and also System.Linq. I did mention this in the tutorial but only very briefly, so I see how you missed it. I have updated the post again to make this more explicit and harder to skip over! Thanks again for keeping me on my toes.

Alex

Mate, Do you ever sleep? That took you under 20 minutes to patch and release.... At least it keeps out away from White Hart Lane and out of trouble ;-)

Bruce Mcleod

All done, Really useful being able to build the RSSReader and learn as I went rather than just being able to download the solution and run it. In computing all real learning comes from staring down problems :-) Last problem I had was solved by adding a Register attribute to MyTableViewController. Without it I kept getting the message "Unknown class MyTableViewController in Interface Builder File" Code looks like - namespace RssReader { [MonoTouch.Foundation.Register("MyTableViewController")] public partial class MyTableViewController : UITableViewController { Thanks so much for this post. A lot of people are going to really get a head start. AllTop says 1400 iTouch Apps were passed for publication last Friday alone. I wonder if viral is a good enough word anymore!

Bruce Mcleod

@Bruce: glad you got the Rss Reader working. I have asked some people on the MonoTouch team about your last comment, because I didn't have to add the [Register] attribute on MyTableViewController. I was wondering why you had to do this. When I gave the UITableViewController a name in Interface Builder (remember we dragged the UITableViewController and then named it "MyTableViewController") and clicked Save, an empty partial class is created in my MainWindow.xib.designer.cs automatically like this: [MonoTouch.Foundation.Register("MyTableViewController")] public partial class MyTableViewController : UITableViewController { } I think that you don't have the latest version of MonoDevelop - if you did, that step would have been done automatically for you. And yes, I do sleep, but this is the first tutorial I've done so I want to make sure it has no mistakes in :-)

Alex

Would it be possible to get a link to download your source? Mine isn't working properly and it would be nice to compare them to see what I am missing.

Alan

@Alan: I have attached a ZIP, check the top of the post. What was the error you were getting? Post details if you fix the problem you were having.

Alex

I wasn't getting an error per se. I just couldn't get the reader to load the headlines. I don't think my simulator is able to access the internet. I am new to iphone development and macs in general. Is there a setting to allow the simulator to access the internet or should it just connect as long as the computer has a connection? Anyway, I placed a rss xml file on my local web server and was able to get it to read that. Thanks for posting the source.

Alan

Hi Alex - Thanks for the tutorial. I enjoyed following along rather than just looking at an already developed solution. I also had to decorate MyTableViewController with the [MonoTouch.Foundation.Register()] attribute. That was the only thing that took a little while to figure out. I wish I would've read Bruce's comments before hand. Other than that the code worked like a charm! On to part 2.... Thanks again!

Danny

Great post - .NET on the iPhone is the way forward for sure. I also had the problem where the TableViewController wasn't generated in the MainWindow.xib.designer.cs however the uitvc template (very useful btw) decorates the generated class so that wasn't a problem at all. Everything else worked very well except you should change this line: List NewsArticles; to List<NewsArticle> NewsArticles; Keep up the good work! Kingsley

Kingsley Faulkner

@Kingsley The List<NewsArticle> line of code was the victim of HTML encoding! Thanks for pointing it out, it's corrected now.

Alex

MonoTouch Comparison with Apple Tools I have been playing around with MonoTouch trying to figure out if I liked the new approach to building

One More Red Nightmare

Pingback from blog.kevfoo.com Fun with Monotouch and Multi-Level Table Views without Interface Builder - KevFoo

blog.kevfoo.com

Social comments and analytics for this post This post was mentioned on Twitter by alex_york: Blogged: part 1 of a #MonoTouch tutorial with a UINavigationController http://bit.ly/i9Xef

uberVU - social comments

Hi all - I had some strange behavior where each time I pushed the same view onto the navigation controller, and then clicked back, monotouch would animate the navigation as if it was popping but the same screen would remain. For example, push screen 1, press back (ok), push screen 1 again, press Back - nothing, press Back again (ok). The cause was hooking TouchDown events on the top view in ViewWillAppear instead of ViewDidLoad()... its obvious now what was happening (each back hooked another delegate to the button). Hope this helps someone else who makes the same mistake.

Geoff

Pingback from oni-ecchi.info Iphone & C#, drole de mariage | Blog d'un développeur multi-support

oni-ecchi.info

Thanks for the tutorial. I fell foul of needing the attribute AND accidentally selecting the table AND accidentally connecting the nav outlet to the table and not the nav control. MonoTouch is great but IMO the reliance on the UI builder means it's too easy to make mistakes.

pauliom

@pauliom: I have gotten used to it now but I found Interface Builder tricky at first too. Craig Dunn's blog over at http://conceptdev.blogspot.com/ has lots of samples where he builds up large UI's purely from code, so check that out if you want to avoid Interface Builder :-)

Alex

Still new to iphone so haven't got the tutorial working yet by following it through, so trying the zip file and get this error at line 68 in MyTableViewController.cs: /Users/stu/Downloads/RssReader/RssReader/MyTableViewController.cs(45,45): Error CS0508: `RssReader.MyTableViewController.TableDelegate.EditingStyleForRow(MonoTouch.UIKit.UITableView, MonoTouch.Foundation.NSIndexPath)': return type must be `MonoTouch.UIKit.UITableViewCellEditingStyle' to match overridden member `MonoTouch.UIKit.UITableViewDelegate.EditingStyleForRow(MonoTouch.UIKit.UITableView, MonoTouch.Foundation.NSIndexPath)' (CS0508) (RssReader)

Stu

Woo - got this working... getting used to being a novice again, eyes had skipped some parts of the tutorial the first time.

Stu

Pingback from knowing.net Knowing .NET » Blog Archive » Recently Bookmarked

knowing.net

iPhone Development iPhone Development

Bill Morefield