Setting a common background image on a dozen screens Is there something as "they have too little in common to use a superclass"?

In my case, doing iOS programming, in a project, every screen has the same background. So I created a superclass which only has:

.h

@property (nonatomic) IBOutlet UIImageView *backgroundImageView;


.m

- (void)viewDidLoad {
    [super viewDidLoad];

    UIImage *backgroundImage = ...;
    [self.backgroundImageView setImage:backgroundImage];
}


Is this too little to use a superclass for?

If I didn't add it here, I'd have about 10 view controllers where I would have to do this. (Obviously code duplication).
The short answer is, no, this is not too little code to "extract" into a super class.

If you intend for this bit of code to always be the same throughout every use of this class and its subclasses, then it's absolutely appropriate to put the code in the superclass.  The primary point is, here, if we ever decide to change something about this code, we can change it in once place and have this change applied across all instances.

With that said, however, I want to point out some problems.



As a start, we have no guarantee that this code will actually run.  If our subclass implements viewDidLoad and fails to call to super, now our code doesn't run.  There's still no way to make this guarantee, but there is a way to make a reminder for ourselves.

As this StackOverflow answer outlines, the NS_REQUIRES_SUPER directive will throw a warning when subclass implement the method without calling the super method.  So we should add the following to our .h file:

- (void)viewDidLoad NS_REQUIRES_SUPER;


Now, a subclass of this class which implements viewDidLoad will throw a compiler warning until [super viewDidLoad]; is added to its viewDidLoad implementation.



There's problems still however.  For starters, as a rule, I don't like IBOutlets in my header file.  There's absolutely no reason for these to be public.  But in this particular case, we shouldn't have an IBOutlet at all.  Using an image view set up in interface builder and requiring it be hooked up to this outlet will supremely complicate things.

First, not only do we have to remember to call to super, which I already address.  But we also have to remember to add the image view.  And we have to remember to hook the image view up as an outlet.  Trouble is though, we can't (or shouldn't) hook it up to our current class.  We should open this, the super class, in assistant editor and hook it up to that.  Problem is, assistant editor won't automatically load super class's in the assistant window... so that's kind of a pain.

I'm a huge proponent of using interface builder.  It does a lot to vastly simplify our written code, but in this case, it doesn't suffice for producing simplified results.

We should remove this outlet altogether and not worry in the slightest about relying on the subclass setting up the image view in interface builder and hooking it up properly.  The whole point of subclassing is minimizing redundancy, and so far, we're only half way there.

(Moreover, what guarantee do we have that every subclass of this class will even use interface builder at all?  View Controllers don't require a corresponding interface builder representation--the view can be set up entirely programmatically.)

We need to change our viewDidLoad code to manually create an image view with an image and load it as the background.



The number one problem we'll run into in manually creating and adding the image view to the view controller is getting it appropriately as the background.  We can't be sure whether the subclass will do it's set up first then call super, or call super then do its set up (in case you're wondering, in viewDidLoad, it's appropriate to call to super FIRST, then add your implementation).  Moreover, the view controller will almost certainly have views added on it in interface builder if the developer is using interface builder.

So, we need to add the view as the bottom-most view in the view controller's view's subviews.

This might be a good implementation:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Set up image view
    UIImage *backgroundImage = [UIImage imageNamed:@"background"];
    UIImageView *backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
    [backgroundView setTranslatesAutoresizingMaskIntoConstraints:NO];
    // aspect fill may be preferred
    backgroundView.contentMode = UIViewContentModeScaleAspectFit; 

    // Add background view as back-most view
    [self.view insertSubview:backgroundView atIndex:0];

    // Set up auto layout constraints so this view controller is applicable
    // on any device in any rotation
    NSDictionary *views = NSDictionaryOfVariableBindings(backgroundView);

    NSArray *verticalConstraints = 
        [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[backgroundView]-0-|"
                                                options:0
                                                metrics:nil
                                                  views:views];
    NSArray *horizontalConstraints = 
        [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[backgroundView]-0-|"
                                                options:0
                                                metrics:nil
                                                  views:views];

    [self.view addConstraints:verticalConstraints];
    [self.view addConstraints:horizontalConstraints];
}




I'm not particularly a fan of setting up UI in code (especially auto layout).  I much, much prefer interface builder.  But in some cases, it is absolutely necessary to get it right.

This auto layout code may seem bulky and unnecessary if your current app is just for iPads and just for a single orientation.  But as soon as you want to subclass this view controller into an app that rotates or is made for iPhones, or is a universal app, you'll be glad to have this auto layout code.



As one final comment, I might make a UIImage property in the .h file so that the background image could be changed.