Handling Subviews Of The Main View In UIViewController: Part 1

We often want to create subviews of the main view dynamically. In this two – part blog, we’ll explore best practices for doing this, and how to avoid some difficult to trace errors along the way. So, let’s get started!

Start Xcode, Select “Create a new Xcode project,” and choose the Single View Application template. Click Next, name the project “SubviewTest”, and select options as shown:

 

Creating The Project

 

Click Next, choose a location to save the project, and click Create.

 

 

We’re going to create two UIView instances and add them to the ViewController’s view as subviews. Create two new UIView classes by selecting File | New > File… from the menu. Use the Objective – C Class template, make sure they are both subclasses of UIView, and name them View1 and View2. Next, (again using File | New > File…) select the Objective – C Protocol template, and create a new protocol named ViewDelegate.

 

Open ViewDelegate.h and enter the following code:

 

#import <Foundation/Foundation.h>



@protocol ViewDelegate <NSObject>



-(void) view1ButtonPressed;

-(void) view2ButtonPressed;



@end

 

 

Now open View1.h, and enter:

 

#import <UIKit/UIKit.h>

#import "ViewDelegate.h"



@interface View1 : UIView



@property (nonatomic, strong) UIButton *button;

@property (nonatomic, assign) id <ViewDelegate> delegate;



@end

 

View2.h will look very similar:

 

#import <UIKit/UIKit.h>

#import "ViewDelegate.h"



@interface View2 : UIView



@property (nonatomic, strong) UIButton *button;

@property (nonatomic, assign) id <ViewDelegate> delegate;



@end

 

 

As we can see, both views will have a delegate object that will respond to messages in ViewDelegate.h.

 

Now Edit View1.m:

 

#import "View1.h"



@implementation View1



@synthesize button;

@synthesize delegate;



-(id)initWithFrame:(CGRect)frame

{

self = [super initWithFrame:frame];

if (self) {

self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];

CGRect buttonFrame = CGRectMake(50, 50, 100, 35);

self.button.frame = buttonFrame;

[self.button setTitle:@"One" forState:UIControlStateNormal];

[self.button addTarget:self

action:@selector(buttonTouched)

forControlEvents:UIControlEventTouchUpInside];

[self addSubview:self.button];

}

return self;

}



-(void)buttonTouched

{

[self.delegate view1ButtonPressed];

}



@end

 

 

We override View1’s initWithFrame: method in order to dynamically place a UIButton on the view. We set the title of the button to “One,” and add an action method for the event Touch Up Inside before adding the button as a subview. The buttonTouched action just calls the delegate method.

 

View2.m is again, very similar:

 

-(id)initWithFrame:(CGRect)frame

{

self = [super initWithFrame:frame];

if (self) {

self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];

CGRect buttonFrame = CGRectMake(50, 50, 100, 35);

self.button.frame = buttonFrame;

[self.button setTitle:@"Two" forState:UIControlStateNormal];

[self.button addTarget:self

action:@selector(buttonTouched)

forControlEvents:UIControlEventTouchUpInside];

[self addSubview:self.button];

}

return self;

}



-(void)buttonTouched

{

[self.delegate view2ButtonPressed];

}



@end

 

 

Now that we have two views, let’s add them to our ViewController. Open ViewController.h and edit it as follows:

 

 

#import <UIKit/UIKit.h>

#import "View1.h"

#import "View2.h"



@interface ViewController : UIViewController <ViewDelegate>



@property (nonatomic, strong) View1 *view1;

@property (nonatomic, strong) View2 *view2;



@end

 

 

The ViewController class adopts the ViewDelegate protocol before setting up two properties for the subviews.

 

Here is ViewController.m:

 

#import "ViewController.h"



@interface ViewController ()



@end



@implementation ViewController



@synthesize view1, view2;



- (void)view1ButtonPressed

{

NSLog(@"Button 1 Touched");

}



- (void)view2ButtonPressed

{

NSLog(@"Button 2 Touched");

}



- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

if (self) {

CGRect view1Frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height / 2);

self.view1 = [[View1 alloc] initWithFrame:view1Frame];

self.view1.backgroundColor = [UIColor redColor];

[self.view addSubview:self.view1];

CGRect view2Frame = CGRectMake(0, self.view.bounds.size.height / 2, self.view.bounds.size.width, self.view.bounds.size.height);

self.view2 = [[View2 alloc] initWithFrame:view2Frame];

self.view2.backgroundColor = [UIColor blueColor];

[self.view addSubview:self.view2];

}

return self;

}



- (void)viewDidLoad

{

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

self.view1.delegate = self;

self.view2.delegate = self;

}



- (void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

view1 = nil;

view2 = nil;

}



@end

 

 

First, we define the two delegate methods. Here, we just log out the fact that the buttons were pressed, but we could take any action. Next we override initWithNibName: bundle: to create the two subviews and give them properties before adding each as a subview to the view controller’s view. Finally, we set the view controller as the delegate of view1 and view2 in viewDidLoad.

 

If we run the app now, The views will be displayed in their proper colors and with their respective buttons, but touching the buttons appears to do nothing. Let’s add a log statement to each view’s button handler to see if they’re getting called:

 

In View1:

 

-(void)buttonTouched

{

NSLog(@"button in view 1 is pressed");

[self.delegate view1ButtonPressed];

}

 

 

and in View2:

 

-(void)buttonTouched

{

NSLog(@"button in view 2 is pressed");

[self.delegate view2ButtonPressed];

}

 

 

Run the app again, and note the result in the console:

 

Console Output

 

 

 

Obviously, the button handlers are working, but for some reason, the delegate methods aren’t getting called in the ViewController! Before continuing with the second part of the blog, try to figure out why these delegate methods aren’t getting called.

 

 

Output

 

Leave a Comment: