How to add multiple subviews to my iOS view in one line of code? (via "monkeypatching" an `addSubviews` instance method to UIView perhaps?)

StackOverflow https://stackoverflow.com/questions/21963747

Question

lets just say I had just one UILabel subview element & one UITextView subview element inside of a given ViewController.m's viewDidLoad method like so:

UILabel *name = [[UILabel alloc] initWithFrame:CGRectMake(20, 140, 280, 40)];
name.text = @"Name: Eric";
UITextView *bio = [[UITextView alloc] initWithFrame:CGRectMake(20, 190, 280, 80)];
bio.text = "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature fro...";

In a normal fashion I could add them one at a time:

[self.view addSubview:name];
[self.view addSubview:bio];

But if there were several dozen or perhaps hundreds of UI components, adding each of them one at time would add dozens to hundreds of lines of code to my project.

Coming from a ruby background, I believe brevity is very important. Is there any way to pass in my UI elements as an array like so?:

[self.view addSubviews:@[name, bio]];

It might be a little early for me to start patching iOS's libraries at boot time but that would definitely be an acceptable solution to me.

Coming from a ruby/rails template of understanding, I'm wondering, is there something equivalent to Rail's /initializers folder in iOS applications? FYI non/pre-rails folks, the /initializers folder in rails is scanned by rails at the beginning of any rails session, and all code inside its .rb files is executed before anything else in rails is loaded aside from the dependencies libraries which are loaded just before that. Thus the ActiveRecord gem (library) for example could then be reinstantiated in an initializer and be amended to at that point. I know Objective C is immutable in a lot of cases though and don't know if that would be a problem.

Existing Cocoa library methods are of course preferred.

In ruby I'd probably patch the method to UIViewController maybe a little something like this:

class UIViewController
  def addSubviews(subviews)
    subviews.each {|obj| self.view.addSubview(obj) }
  end
end
Was it helpful?

Solution

I'd go for a category on UIView.

File --> New --> File --> Objective-C category. Name it something like EasyAdditionOfSubviews, make it a category on UIView.

UIView+EasyAdditionOfSubviews.h

#import <UIKit/UIKit.h>

@interface UIView (EasyAdditionOfSubviews)

- (void)addSubviews:(NSArray *)views;

@end

UIView+EasyAdditionOfSubviews.m

#import "UIView+EasyAdditionOfSubviews.h"

@implementation UIView (EasyAdditionOfSubviews)

- (void)addSubviews:(NSArray *)views
{
    for (UIView *view in views) {
        if ([view isKindOfClass:[UIView class]]) {
            [self addSubview:view];
        }
    }
}

@end

This way, you just #import UIView+EasyAdditionOfSubviews.h anywhere you want to be able to add an array of views at a time.

And you're better off making sure that what you're passing to addSubview: is indeed a UIView, otherwise you risk a crash (for example, try passing it an NSArray).

But to address your wider concern:

But if there were several dozen or perhaps hundreds of UI components, adding each of them one at time would add dozens to hundreds of lines of code to my project.

If you're building a UI programmatically, you want to be exactly sure what, how, and when subviews are added to a view; it's perfectly fine to be explicit when adding a subview to a view.

As a matter of fact, your workaround to not explicitly adding the subview to its superview is... to explicitly add it to an array, which you then add to the superview. Not good for brevity!

OTHER TIPS

Assuming subViews is an array, you could do:

[subViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [superview addSubview:obj];
}];

As @pranav mentions, an alternative is to use the for..in syntax:

for (UIView* view in subViews) {
    [superview addSubview:view];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top