Subclassing both UIViewController and UITableViewController

990 views
Skip to first unread message

Sebastian Celis

unread,
Sep 7, 2010, 2:36:32 PM9/7/10
to cocoa-unbound
Greetings,

I have created custom subclasses for both UIViewController and
UITableViewController. These subclasses serve to provide generic
functionality for laying out subviews, performing animations, handling
device rotations, and more. They implement standard UIViewController
methods like viewWillAppear: and
allowAutorotateToInterfaceOrientation:, as well as define their own
properties and methods.

These classes are almost completely identical. They contain many of
the same properties and methods. Their only differences are centered
on UITableView-specific code in the UITableViewController subclass.

Is there any good, simple way to avoid duplicate code in this
scenario? Keeping these classes in sync has become more of a pain,
lately.

Many thanks,

Sebastian

Aaron Brethorst

unread,
Sep 7, 2010, 2:41:28 PM9/7/10
to cocoa-...@googlegroups.com
I've had the same problem. The two (inadequate) solutions I've used are:

1. Doing the exact same thing as you.
2. Reimplementing UITableViewController's functionality on top of UIViewController. In other words, if I had CustomViewController : UIViewController, I would then create:

@interface CustomTableViewController : CustomViewController <UITableViewDataSource, UITableViewDelegate> {
UITableView *tableView;
}

That sucks too.

Categories won't cut it. Multiple inheritance doesn't exist.

I suppose you could also do something horrifying involving preprocessor macros, sort of along the lines of how SynthesizeSingleton injects a bunch of code into classes that use it.

Bill Garrison

unread,
Sep 7, 2010, 2:45:33 PM9/7/10
to cocoa-...@googlegroups.com

On Sep 7, 2010, at 2:36 PM, Sebastian Celis wrote:

> Greetings,
>
> I have created custom subclasses for both UIViewController and
> UITableViewController.

[snip]

>
> These classes are almost completely identical. They contain many of
> the same properties and methods. Their only differences are centered
> on UITableView-specific code in the UITableViewController subclass.
>
> Is there any good, simple way to avoid duplicate code in this
> scenario? Keeping these classes in sync has become more of a pain,
> lately.

Can your table view controller work as a subclass of your custom view controller?

I.e. your inheritance hierarchy becomes:

UIViewController --> MyUIViewController --> MyTableViewController

If your table view controller really duplicates a lot of view controller functionality, basing one off of the other seems like a decent solution.

Bill

Bill Garrison

unread,
Sep 7, 2010, 2:54:01 PM9/7/10
to cocoa-...@googlegroups.com

On Sep 7, 2010, at 2:41 PM, Aaron Brethorst wrote:

> I've had the same problem. The two (inadequate) solutions I've used are:
>
> 1. Doing the exact same thing as you.
> 2. Reimplementing UITableViewController's functionality on top of UIViewController. In other words, if I had CustomViewController : UIViewController, I would then create:
>
> @interface CustomTableViewController : CustomViewController <UITableViewDataSource, UITableViewDelegate> {
> UITableView *tableView;
> }
>
> That sucks too.

Not sure that I buy the suckiness assertion. Does UITableViewController really do SO much more than the core UIViewController?


@interface CustomTableViewController : CustomViewController <UITableViewDataSource, UITableViewDelegate> {

UITableView *_tableView;
}

@property (nonatomic, retain) IBOutlet UITableView *tableView;
@end

@implementation CustomTableViewController
@synthesize tableView = _tableView;

- (void) viewDidLoad {
[super viewDidLoad];
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:[UITableView class]]) {
self.tableView = view;
break;
}
}
}

- (void) viewDidUnload {
[super viewDidUnload];
self.tableView = nil;
}

- (void) dealloc {
[super dealloc];
[_tableView release];
}
@end

Aaron Brethorst

unread,
Sep 7, 2010, 2:56:32 PM9/7/10
to cocoa-...@googlegroups.com
Reimplementing UITableViewController feels inelegant to me. Looking at UITableViewController.h just now indicates that you should make sure your subclass does the following:

// Creates a table view with the correct dimensions and autoresizing, setting the datasource and delegate to self.
// In -viewWillAppear:, it reloads the table's data if it's empty. Otherwise, it deselects all rows (with or without animation) if clearsSelectionOnViewWillAppear is YES.
// In -viewDidAppear:, it flashes the table's scroll indicators.
// Implements -setEditing:animated: to toggle the editing state of the table.

I suppose that's not so bad, but I'm still turned off by it (of course, I've done exactly this in shipping code).

Sebastian Celis

unread,
Sep 7, 2010, 3:00:28 PM9/7/10
to cocoa-unbound
Hi Bill and Aaron,

Thanks for the responses.


On Sep 7, 1:45 pm, Bill Garrison <1billgarri...@gmail.com> wrote:
> Can your table view controller work as a subclass of your custom view controller?
>
> I.e. your inheritance hierarchy becomes:
>
> UIViewController --> MyUIViewController --> MyTableViewController
>
> If your table view controller really duplicates a lot of view controller functionality, basing one off of the other seems like a decent solution.

That's definitely possible, though it doesn't seem ideal as I would
then have to re-implement all of Cocoa's UITableViewController
functionality. Granted, this probably wouldn't be a lot, but there is
a list of things UITableViewController does above and beyond
UIViewController in the UITableViewController documentation. And who
knows what Apple might be doing that isn't on this list? It seems like
going down this road might be opening up a can of worms later as I
find more and more things that MyTableViewController does that
UITableViewController does not do.

Thanks,

Sebastian

Jonathan S

unread,
Sep 7, 2010, 2:59:41 PM9/7/10
to cocoa-unbound
I love how UITableViewController handling scrolling property than
doing it in UIViewController.

If anyone can find a trick of that, it be nice.

you could move UITableViewDataSource into a different file like in
NSObject.

Rafael Bugajewski

unread,
Sep 7, 2010, 4:51:55 PM9/7/10
to cocoa-...@googlegroups.com
On 07.09.2010, at 21:00, Sebastian Celis <seba...@sebastiancelis.com> wrote:

> That's definitely possible, though it doesn't seem ideal as I would
> then have to re-implement all of Cocoa's UITableViewController
> functionality. Granted, this probably wouldn't be a lot, but there is
> a list of things UITableViewController does above and beyond
> UIViewController in the UITableViewController documentation. And who
> knows what Apple might be doing that isn't on this list? It seems like
> going down this road might be opening up a can of worms later as I
> find more and more things that MyTableViewController does that
> UITableViewController does not do.

I think you are exaggerating. Reimplementing the UITableViewController functionality you really need shouldn't be more than you've seen in one of the older posts in this thread.

Rafael

Luke Redpath

unread,
Sep 7, 2010, 5:18:29 PM9/7/10
to cocoa-unbound
I'm curious to know what the common code between the two sub-classes
is; it sounds like you're running into a common problem with
inheritance and it might be a sign that composition would provide a
better solution. If you can decompose/refactor your common
functionality into new objects that your custom UIViewController sub-
class can delegate to, you might find a better solution.

Of course, without knowing exactly what your custom behaviour is, its
hard to say.

Hamish Allan

unread,
Sep 7, 2010, 5:36:48 PM9/7/10
to cocoa-...@googlegroups.com

AFAICR, calling -initWithNibName:bundle: on a UITableViewController gives you a view controller with self.tableView = nil. That is to say, you can just drop the UIViewController subclass, and test for self.tableView in cases in where you need differing behaviour.

@hatfinch

Sent from my iPad (otherwise I would have tested this myself rather than saying "AFAICR"!)

Jim Dovey

unread,
Sep 7, 2010, 5:45:51 PM9/7/10
to cocoa-...@googlegroups.com
On 2010-09-07, at 5:36 PM, Hamish Allan wrote:

>
> AFAICR, calling -initWithNibName:bundle: on a UITableViewController gives you a view controller with self.tableView = nil. That is to say, you can just drop the UIViewController subclass, and test for self.tableView in cases in where you need differing behaviour.

UITableView throws an exception if its view isn't a UITableView.

Hamish Allan

unread,
Sep 7, 2010, 6:01:10 PM9/7/10
to cocoa-...@googlegroups.com

Sent from my iPad
In which case just grab one of the many UITableViewController re-implementations floating around without this obnoxious 'feature'. (You can tell it's a while since I've used a 'real' UITableViewController!)

H

Nathan de Vries

unread,
Sep 7, 2010, 7:33:23 PM9/7/10
to cocoa-...@googlegroups.com
On 08/09/2010, at 5:00 AM, Sebastian Celis wrote:
> That's definitely possible, though it doesn't seem ideal as I would
> then have to re-implement all of Cocoa's UITableViewController
> functionality.

There really isn't a lot to it, and the benefits far outweigh the work involved. It would resolve your common UIViewController subclass use-case, and you'd also have complete control over your UIViewController's root view which is important when you're building more complicated interfaces.

Matt Gallagher's re-implementation is pretty thorough, so I'd take a look at that first:

http://cocoawithlove.com/2009/03/recreating-uitableviewcontroller-to.html


Cheers,

Nathan de Vries

Stefan Arentz

unread,
Sep 7, 2010, 10:11:48 PM9/7/10
to cocoa-...@googlegroups.com

UITableViewController is *very* simple to implement.

Why not simply just subclass your custom UIViewController and implement the UITableViewController behaviour that is described in the documentation. I would be surprised if it is more than few screens of code.

S.

Sebastian Celis

unread,
Sep 7, 2010, 11:01:14 PM9/7/10
to cocoa-unbound
Here is some of what my subclasses currently do (this list seems to be
growing):

*) Handles device rotation so that whether or not a rotation should be
allowed is data-driven instead of code-driven.

*) Handles layout of subviews, especially when rotation is concerned.
I have found that there are situations where the autoresizing mask
simply does not cut it. See https://devforums.apple.com/message/238006#238006
for more information.

*) If there is a keyboard displayed, my subclasses contain knowledge
of this fact, as well as the size of the keyboard, so that the view
can better layout its subviews.

These all hook directly into UIViewController methods like
viewWillAppear: and
willAnimateRotationToInterfaceOrientation:duration:. It would
definitely be possible to put the majority of my custom logic into a
different, helper object. Though we would still be stuck with a
UIViewController subclass and a UITableViewController subclass
containing identical code -- namely calling out to the same helper
object from the standard UIViewController methods.


So, from the responses I have gotten, it seems like the general
consensus is to implement my own table view controller. I agree that
it shouldn't be difficult, it just feels wrong. But yeah, it probably
is the best solution given the simplicity of UITableViewController.

Thanks, everyone, for your input. I appreciate it.

- Sebastian

Joachim Bengtsson

unread,
Sep 9, 2010, 3:19:08 PM9/9/10
to cocoa-...@googlegroups.com
<MyViewControllerCommon.m>
// All the custom stuff, without an @implementation scope
// Don't add this file to your target.

<MyViewController.m>
@implementation MyViewController
#include "MyViewControllerCommon.m"
@end

<MyTableViewController,m>
@implementation MyTableViewController
#include "MyViewControllerCommon.m"
// Table view controller specific stuff
@end

Reply all
Reply to author
Forward
0 new messages