views:

1041

answers:

4

Hi,

I have an application that, on load, displays a UITableView with the user's data in it.

However, when the user first loads the application (before they've created any data), I'd like to display, instead of an empty table, a background image (with an arrow pointing to the 'add a record' navbar button). Once the user has added their first record, the tableview is displayed instead. I've seen numerous apps do this - the only example I can think of at present is Cha-Ching, before you have any budgets/etc set up. I can't for the life of me work out how to do this, though.

I initially added a navigationcontroller to the main window's xib, the rootViewController of which was a custom viewcontroller/xib. This rootViewController contained the background image with a hidden tableview above it, and a custom tableviewcontroller that managed the tableview. This seemed to work just fine, and if there was data it would load and display in the table. However, if I was to scroll the data offscreen, the app would crash, with this error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'*** -[UITextEffectsWindow tableView:cellForRowAtIndexPath:]: unrecognized selector sent to instance 0xd2d130'

I have no clue what a UITextEffectsWindow is, or why it was trying to manage my tableview. I presume something may be hooked up incorrectly in my view hierarchy...

If there's a much simpler/more straightforward way of doing this, I'd be very grateful if someone could explain it. How would you do this?

Thanks in advance.

+1  A: 

Here's one solution that I've been satisfied with so far.

First, I created a transparent view that was the same size as my TableView. I add this view as a sibling of the TableView whenever the TableView's data source has no data in it. I completely remove the view when data is available because transparency can affect the smoothness of the scrolling animation with TableViews.

I simply added a transparent label to this view that says something to the effect of "No Data Available". Adding a little shadowing to this label really helped to reinforce the concept that the label was 'floating' over top of the empty TableView.

I like your method of using an image though. Done properly, it might save you some work if you don't have to localize a string like I currently do.

Jonathan Arbogast
I just tried adding the UIImageView to the TableViewController's xib file - however, even if the tableview is hidden, the image doesn't display. Presumably this is because the view outlet of the tableviewcontroller is set to the tableview itself.What do you mean by adding a view as a sibling of the tableview? It sounds like this might be a possibility...
James Frost
I created a custom "EmptyTableView" class which contained the label with all the proper transparency and shadowing effects in place.Then in my ViewController I created one of these EmptyTableViews inside of loadView. I added and removed this EmptyTableView from my ViewController's view as appropriate. In my case I have a tableView as part of an overall custom view. In your case I think the tableView == self.view. I might try adding an EmptyView to tableView and [tableView superView] and see if you like either result.Perhaps someone else has a better solution using Interface Builder...
Jonathan Arbogast
Thanks - I'll give this a go when I get home tonight. Do you swap out the EmptyTableView for a real TableView once your data has loaded? And is the data in your TableView managed by another, TableViewController class (rather than the ViewController you mention)?I think I'm partly getting confused because from what I've seen, a TableViewController's view *must* be a TableView, right? In which case, I can't work out how I can add other UI elements to that view.... Hopefully my brain will untangle itself by this evening!
James Frost
You are correct in thinking that a TableViewController has a TableView as its view. In my case, I'm using a subclass of ViewController that I created (MyViewController). MyViewController's view always has a TableView as one subview. Essentially, if the table has no data, I add another subview (EmptyTableView) which is just a subclass of UIView that contains my "floating" label. Otherwise, I remove the EmptyTableView from the view hierarchy.Does that help?
Jonathan Arbogast
I think it does - thanks. I just found this Pragmatic Programmers' forum entry, which has also proved helpful: http://forums.pragprog.com/forums/83/topics/1606I think the best solution might be to have, as you say, a ViewController that has a TableView as a subview (and my UIImage placeholder as another subview). The ViewController can conform to the TableViewDelegate and DataSource protocols, and manage the table. Being a ViewController (rather than TableViewController) would let me manage other views more easily. I'll report back when I've tried this out.
James Frost
A: 

If you use Three20, you can easily set any image you want as a place holder prior to your table being populated.

Corey Floyd
I'd like to try and write a solution myself, instead of using a library for something which (I would've thought) should be quite simple. Thanks for pointing out Three20, though - it looks rather neat!
James Frost
It is pretty versatile... You could write it yourself, but thats kind of missing the point of object oriented programming: not to write it again if you don't have to. The creator of Three20 realized this wasn't simple as it should be (asynchronous tableviews) and made something that was easier to use. For instance what you are trying to do can be accomplished in one line of code using Three20.
Corey Floyd
Be aware, though, that Three20 often reinvents the wheel and tries to reimplement everything it's author did not like about iPhone. While it is quite powerful, it is somewhat bloated, and adds bloat to your application. Also not every class from Three20 has equal quality… so please do apply your brain to any third-party code you reuse.
Andrey Tarantsov
Three20 does go out of its way to abstract the cocoa classes in a completely different way. You do get some ease in implementing the things Three20 does well. However, if you need slightly different functionality then provided, subclassing is not easy. I spent a weekend on it, but I free got asynchronous loading or pictures and a photo browser for my time.
Corey Floyd
A: 

So, to solve this I did as discussed in the comments above:

I created a normal UIViewController subclass, which contained a UIImageView and a UITableView. The viewController conforms to the UITableViewDelegate and UITableViewDatasource protocols, and looks after the tableView. The viewController class simply shows or hides the imageView depending on whether data is available.

I was going wrong before by trying to manage both these views with a UITableViewController. A UITableViewController has to have a tableView as its view, whereas, with this solution, a viewController can contain both the image and the tableView, and implement the necessary protocols to manage the tableView.

Thanks for all the help!

James Frost
+1  A: 

To achieve this using a UITableViewController subclass as my only view (within a UINavigationController as per the Apple template) I used the following approach:

  1. Create a UIView of the size of my tableView in the XIB that contains your UITableViewController and tableView.

  2. Add an ImageView set with my placeholder image to the UIView.

  3. Wire up the UIView as an IBOutlet (in the example code below, I called it emptyTableView)

  4. When it is time to show the placeholder from within the UITableViewController subclass :

    [self.tableView addSubView:emptyTableView]; [self.tableView setScrollEnabled:FALSE];

Disabling the scroll is necessary otherwise the user will be able to move the placeholder image up and down. Just remember to enable it once the user adds an item.

  1. To remove the image view

    [emptyTableView removeFromSuperview];

Cocoanut