Creating a view programmatically

April 29th, 2009 by Chris Leave a reply »

Now it’ time for something more advanced. In Interface Builder how-to I told you, that I.B. is great for designing interfaces, but there are cases this knowledge – creating everything programmatically will be necessary and helpful, like:

  • there is an unknown number of object which will be added
  • the number of object is static but enormous, and they are almost the same with tiny differences
  • you want to be better than others and you like hard not-needed extra work…

Let’s start. So far you created everything in Interface Builder, you were editing NIB (.xib) files, but in fact Interface Builder created the whole code for you in the background.

As I mention in article about I.B. I told you, that you can create the Interface only in I.B., partialy in I.B. and Xcode or only in Xcode.

I personally advice you to use Interface Builder wherever possible using Xcode when necessary, if some properties cannot be set in I.B.. In this article I will show you how to create everything using only Xcode. Let’s start.

Whenever you created any object to be visible in Interface Builder you used the keyword IBOutlet for example: IBOutlet UILabel *myLabel;. If you customize in this case myLabel in Xcode only, you can skip this keyword, so: UILabel *myLabel;. The same with any methods / actions, you were using IBAction what in fact is an equivalent to void (no value returned), but IBAction gives you one advantage – that it’s visible in I.B.. So if are impementing a method that will be called by objects created only in Xcode you can use: -(void)myAction; instead of -(IBAction)myAction; and of course if you need to know what object called the method: -(void)myAction:(id)sender instead of -(IBAction)myAction:(id)sender.

Creating a view programmaticaly

Let’s start by creating programmaticaly a view. Let’s add a UIViewController class, name it MyView.m (and MyView.h). In you AppDelegate’s header add standard lines:

#import "MyView.h"
@class MyView;

and in AppDelegate’s implementation:

MyView *viewController = [MyView alloc];
[window addSubview:viewController.view];

Now go to MyView.m, and find somewhere the commented loadView method, it should look like:

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/

Deleate /* and */ to activate this method. During allocating of this class (or any UIViewController) the loadView will be automatically called to load the view (and BTW, once view is loaded viewDidLoad is called). Extend loadView class by these two lines:

	self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
	self.view.backgroundColor = [UIColor yellowColor];

If you Build and Go you will get the empty yellowyellowview screen (with status bar of course).

Why?

It’s simple. first line is creating a new view [UIView alloc]. Because MyView class is UIViewController it already expects a view. So far, you created a view in Interface Builder connecting view outlet to view’s preview, if I can call it like this, this time you created it programmatically. The second part is creating the view’s frame, you are setting the coordinates and size of view. Quick info about CGRectMakes’s arguments:

CGRectMakes(xTopLeftCorner,yTopLeftCorner,width,height)

The 2nd line is quite obvious, just changing the background color. Hmm, but where did you put anything to make this view visible? Answer: in AppDelegate implementation file. So, to be more specific, visit your …AppDelegate.m file:

  • MyView *viewController = [MyView alloc] is creating new object of class MyView and automatically calls loadView method, so you don’t have to
  • [window addSubview:viewController.view] is adding the view created by loadView to the application window

Hope it’s clear now.

Adding a label programmatically

Add this block of code to loadView or viewDidLoad method:

	UILabel *myLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(50, 200, 200, 80)];
	myLabel2.text = @"only Xcode";
	myLabel2.textAlignment = UITextAlignmentCenter;
	myLabel2.textColor = [UIColor yellowColor];
	myLabel2.shadowColor = [UIColor whiteColor];
	myLabel2.shadowOffset = CGSizeMake(1,1);
	myLabel2.font = [UIFont fontWithName:@"Zapfino" size:20];
	myLabel2.backgroundColor = [UIColor greenColor];
	[self.view addSubview:myLabel2];
	[myLabel2 release];

Code is similar to that one, where you created the view. Differences? [UILabel alloc], not [UIView alloc] and another frame which suits me. Next 7 lines is a customization of label – changing label’s text, text alignment, text color, shadow, font, also background color. To show label on view you use [self.view addSubview:myLabel2];. I’ve already told you once, but I will repeat, almost everything visible on iPhone / iPod touch is some kind of view. UILabel is also UIView’s subclass.

The last line contains: [myLabel2 release]; it frees our memory, but use it wisely. This time I didn’t put it into dealloc method that is called when application terminates. Hmmm… no…, read the article about Memory management and you will see the point.

Now your app should look like this:

yellowviewlabel

You can easily add next label, remember to give it an unique name:

	UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 200, 80)];
	myLabel.text = @"Isn't it amazing?";
	myLabel.textAlignment = UITextAlignmentCenter;
	myLabel.backgroundColor = [UIColor colorWithRed:0.2 green:0.9 blue:0.5 alpha:0.3];
	[self.view addSubview:myLabel]; 

Creating a button programmaticaly

Let’s start with the code:

	myButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] initWithFrame:CGRectMake(50, 300, 200, 80) ];
	[myButton setTitle:@"And a button" forState:UIControlStateNormal];
	[myButton setTitle:@"Alert :D " forState:UIControlEventTouchDown];
	[myButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
	myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
	myButton.backgroundColor = [UIColor clearColor];
	[myButton addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
	[self.view addSubview:myButton]; 

As you see above, this time I didn’t use something like [UIButton alloc], but [UIButton buttonWithType:...]. Why? Because I had to, if the button was simply an image I would use alloc, but as long as I want to use the predefined type I had to create it using above command.

Now you might ask a question why I set some properties using [myObject something:], and others using myObject.something=. Answer: some properties can be set using both of these two ways, some only using one. For example  in UILabel:

myLabel.text = @"asdfg";
[myLabel setText:@"asdfg"];

are the equivalents. So comming back to my button, I set two different titles, one is displayed during normal state of button, and the other while it’s being pressed, next color, alignment, backgroundColor. Later I connected an action to the button – in bold is a name of method called when user will TouchUpInside – press the button. All you need to do is to implement the buttonAction method:

-(void)buttonAction {
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Hmmm" message:@"It really works :) " delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil];
	[alert show];
	[alert release];
}

If you would like to have info about what object called the method [(id)sender], add a colon on the end of class name in adding a selector to the button:

	[myButton addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];

And of course:

-(void)buttonAction:(id)sender {
	//...
}

One thing you have to know

When you set a frame using CGRectMakes(TopLeftX,TopLeftY,width,height), or the center using CGPointMake(CenterX,CenterY), you need to know something more. These two x, y coordinates of top left corner or center point, are set according to the view (or it subclass) that our object is subview of . So let’s say you have a view 320×480px, but it is not placed in the center of the screen (window), it’s a little bit to the left. If you add a UILabel as the view’s subview and set the center property of that label equal CGPointMake(160,240), it will be in the exact center of the view, but together with the view moved a little bit to the left.

Conclusion

As you see, designing an interface strictly in Xcode is a little bit tiring, but there will be cases you will have to use it in practise. The difficult maybe the lack of knowledge what property customize some key and less importan parts of given object. Everything can be find in documentation. You can also get a list of all available properties pressing Esc-key after “myObject.” or “[myObject “.

As usuall I prepared a project for you, so if something doesn’t work for you, compare it with mine.

noibend

xcodeproj

Download the project

Advertisement

14 comments

  1. lloyd says:

    There is only one success – to be able to spend your life in your own way.

  2. Karl says:

    Hi,

    Just worked it out – the button needs to be alloc with retain then apply the frame.

    -Karl

    myButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain ];
    myButton.frame = CGRectMake(50, 300, 200, 80);

  3. Karl says:

    Hi,

    Just wondering if you know how to make the button title work in the iPhone 3.0 SDK?

    -Karl

  4. Silverfox says:

    Now what I am having trouble doing is displaying a second view upon clicking the button. Could you describe how to do that?

    • Chris says:

      That’s very easy

      [mainViewHere addSubview:secondView];

      in most cases using UIViewController:

      [self.view addSubview:secondView];

      secondView is your “sedond view” as you described

      • Venky says:

        Hi Chris,
        My need is something similar.
        I created an app (with window and subview) using IB entirely .

        In this app, I now need to create a new view. Do I create a new View Controller class and add the UI elements to this controller’s view (possibly using IB)? And add this second view to the main window ?

        And now if there are 2 views, how do I tell the window which view is displayed ?
        Much Thanks.

        • Chris says:

          Wow a little bit complex for the beginners.

          Forget about next UIViewController if you don’t really need it. UIViewController can handle more than only one UIView object, so if it’s not exremly needed, just use one, one of the views will be refered as self.view, and the other view you must implement in header file: IBOutlet UIView *my2ndView.

          If you have to remove and add them very often I recommand you to live self.view alone – white 320×480/460px and create to views: IBOutlet, UIView, *my1stView, *my2ndView; and add them as a subviews to self.view and remove them when neccessary.

          Personaly, sometimes I create really big apps in one UIViewController if they are only big not comples :) .

          Regards
          Chris

  5. hassen bamri says:

    To avoid using window.xib and create the window of your application programmatically :
    1) in “infos.playlist” delete the last key and value
    NSMainNibFile
    MainWindow

    2) in the class main change the code by setting the last parameter in “retVal” to the name of the delegate of the application.
    ——————————————————————-
    int main(int argc, char *argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    /**/
    int retVal = UIApplicationMain(argc, argv, nil, @”noIBAppDelegate”);
    /**/
    [pool release];
    return retVal;
    }
    —————————————————————————

    3) create the window programatically in the ApplicationDelegate “noIBAppDelegate”:
    ———————————————————————–
    - (void)applicationDidFinishLaunching:(UIApplication *)application {
    // create and set a local window
    UIWindow *localWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]bounds]];
    localWindow.backgroundColor = [UIColor whiteColor];
    // set the window of the application
    self.window = localWindow;
    // release the localWindow
    [localWindow release];

    // create an instance of the view Controller
    MyView *viewController = [MyView alloc];
    // add the ViewControllerView to the Window
    [self.window addSubview:viewController.view];

    // make the window visible
    [self.window makeKeyAndVisible];
    }
    ———————————————————————–

    • Chris says:

      I guess you simply can shorten the code in this way:

      self.window = [[UIWindow alloc]…

      that’s very nice of you to put the complete solution, but as everybody sees it’s quite complex and unnecessary extra work just to delete mainwindow.xib isn’t it?

      regards, Chris

      • James says:

        I agree. I just leave the MainWindow.xib file alone and just instantiate my own view controller like your example.

        However, I did wonder if it was possible to create the UIWindow and the App Delegate programmatically. Now I know!

        I’m surprised by the number of developers that actually use Interface Builder. It’s pretty handy, but I got in the habit of making my own interfaces in Java Swing a long time ago and just never stopped. I think it’s the fact that it gives you a greater control and understanding over how everything pieces together.

  6. Rob says:

    Hi, it seems even when you don’t use Interface Builder, you still need to have that MainWindow NIB created and hooked up correctly. Is there any way to avoid even needing that? Thanks! Rob

    • Chris says:

      Hmmm.

      As I start the new project I never modify anything about MainWindow.xib. Of course you are right, it’s an Interface Builder file that is the bottom layer of your views.

      As you might have noticed in my sample projects, I always create new view controller and add it’s view on the main window either programmatically or in I.B.

      Hmmm, If you read new post on the website, you know that I can’t debug it right now, but I’m pretty sure that you can allocate UIWindow programmatically in the same way you create UIView sth like:

      window = [[UIWindow alloc] initWithFrame:CGRectMake(0,0,320,480)];

      as far as I remember, thare is also something about MainWindow in .plist file.

      So to conclude. I’m sure you can create window only using code, but as long as I’ve never had any need to create window programmatically (as it’s automatically created when choosing a template) I cannot give you any confirmed how-to.

      • Rob says:

        Understood, will give that a shot.

        I was actually trying to take an existing project backwards to only use the code. I went in and removed the “Main nib file base name” entry from the plist file and that’s what may have also contributed to the break.

        Thanks! Rob

Leave a Reply