How to add UI elements programatically to existing nib files - objective-c

I wonder how to add UI elements programatically to existing nib files.
If I create a view programatically in loadView method and I add code like the following, the label displays correctly.
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0,2,150,100)];
[self.view addView:lbl];
But how to add the label to an existing nib file?

As Paul.s pointed out, you need to perform your custom code in viewDidLoad method.
From Apple documentation.
This method is called after the view controller has loaded its
associated views into memory. This method is called regardless of
whether the views were stored in a nib file or created
programmatically in the loadView method. This method is most commonly
used to perform additional initialization steps on views that are
loaded from nib files.
So, in your controller you could do this:
- (void)viewDidLoad
{
[super viewDidLoad];
// your other views here
// call addSubview method on self.view
}
Why do you do this? Because here you are sure that view has loaded in memory and outlets has been set correctly.
On the contrary, about loadView method
If you override this method in order to create your views manually,
you should do so and assign the root view of your hierarchy to the
view property. (The views you create should be unique instances and
should not be shared with any other view controller object.) Your
custom implementation of this method should not call super.
An example could be:
- (void)loadView
{
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
UIView *contentView = [[UIView alloc] initWithFrame:applicationFrame];
contentView.backgroundColor = [UIColor whiteColor];
self.view = contentView;
// call addSubview method on self.view
}
I suggest you to read View Controller Programming Guide for iOS.
Hope it helps.

AFAIK, to modify nib file programmatically is not possible.
You can add view into viewDidLoad of UIViewController method.

Inside viewDidLoad
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0,2,150,100)];
[self.view addSubView:lbl];

Related

UIView Subclass backed by xib with Size Classes wrong frame

I am working on a project for iOS 7.0+ with a storyboard, using Size Classes with AutoLayout and I'm using a UIView subclass backed by a xib file of the same name.
What I'am trying to do is I'am instantiating a UIView from xib programmatically and adding it to a ViewController from a Storyboard. This ViewController has AutoLayout up and running but the UIView I am adding doesn't respect the frame of the ViewController.
I'm instantiating my UIView subclass like this:
tabBarView = [[SHDTabBarView alloc] initWithFrame:CGRectMake(0, self.view.height-50, self.view.width, 50)];
[self.view addSubview:tabBarView];
And inside the subclass I'm using a set up of creating a UIView IBOutlet called container to instantiate it form code like this:
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self == nil) return nil;
[self initalizeSubviews];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self == nil) return nil;
[self initalizeSubviews];
return self;
}
-(void)initalizeSubviews{
NSString *nibName = NSStringFromClass([self class]);
UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
[nib instantiateWithOwner:self options:nil];
//Add the view loaded from the nib into self.
[self addSubview:self.container];
}
This is how my xib looks in the Interface Builder (notice the width of the canvas is 320 px):
And that's how it looks on the iPhone 6 (notice how it's getting cut off from the right side):
I've tried to use a multitude of solutions, including doing it all in code with an open-source solution PureLayout, using a manual constraint set up, etc.
None of my findings seem to work right. Ideally, I want to set up everything in Interface Builder, then just add the view to the superview of the ViewController with according frame and let AutoLayout do its magic.
How should I approach this task? Any advices are more than welcome.
Try to set the frame of your subview in the viewDidLayoutSubviews(). Looks like you init your subview before view fully layouted

How to setup view in UIViewController

I've created an almost-empty UIViewController class named MyViewController. In the viewDidLoad I'm setting the title and adding a close-button to the navigationItem.leftBarButtonItem.
I'm presenting my MyViewController like this:
MyViewController *myViewController = [[MyViewController alloc] init];
UINavigationController *nc = [[UINavigationController alloc] myViewController];
nc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:nc animated:YES];
When the viewController is presented, the background of it's view is just black. How can I setup it's view to fill-out the screen with an empty view -- just like when the UIViewController is setup in a Storyboard?
I've tried adding the following to the viewDidLoad, but the view is still black:
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
By default a VC's view gets created in -loadView which you usually neither call, nor override. It gets called automatically the first time you request the the VC's view property.
The view's size is automatically set to the 'empty space', like everything except for the status bar without a NavigationController, minus the navbar when using one etc. You shouldn't worry about its size - usually it's just fine.
You can add your own views in -viewDidLoad and remove them again (for low-memory reasons) in -viewDidUnload.
You can add an UIImageView with custom image and then sent it to back
So in your viewcontrollers viewDidLoad
UIImageView *back = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"MyBG"]];
[self.view addSubview:back];
[self.view sendSubViewToBack:back];
If you are using a XIB file you should init the view controller through the method initWithNibName: bundle:

Multiple Views in Xcode 4.2

I'm having a lot of trouble finding a tutorial for implementing multiple views in Xcode 4.2 without storyboard, this is for a class so I can't use storyboard yet. I'm just trying to have a 2nd view with a UIPicker come up when a button is clicked in the main view, I just can't find one for this version of Xcode and it's different enough from the older versions to confuse me.
Any help appreciated if someone can give me a quick description of what I need to do this or a newer tutorial I'd appreciate it :)
I think you should read the UIView Programming Guide to get a good handle on how UIViews work exactly. I find nibs/storyboard are really great at confusing new iOS developers.
In essence, a UIViewController has 1 view which you set in the viewDidLoad or loadView method by using the [self setView:someUIView]. You add more stuff to the screen by adding UIViews as a subview of the viewcontroller's "Main" view. For example
-(void)loadView {
// Create a view for you view controller
UIView *mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self setView:mainView];
// Now we have a view taking up the whole screen and we can add stuff to it
// Let's try a button, a UIButton is a subview of UIView
UIButton *newButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect];
// We need to set a frame for any view we add so that we specify where it should
// be located and how big it should be!
[newButton setFrame:CGRectMake(x,y,width,height)];
// Now let's add it to our view controller's view
[self.view addSubview:newButton];
// You can do the same with any UIView subclasses you make!
MyView *myView = [[MyView alloc] init];
[myView setFrame:CGRectMake(x,y,width,height)];
[self.view addSubview:myView];
}
Now here we have our viewController who'se view is just a plain UIView which in turn has 2 subviews; newButton and myView. Since we created the MyView class, maybe it contains subviews as well! Let's take a look at what a UIView subclass could look like:
// Here is the init method for our UIView subclass
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Let's add a button to our view
UIButton *newButton2 = [[UIButton buttonWithType:UIButtonTypeRoundedRect];
// Of course, the frame is in reference to this view
[newButton2 setFrame:CGRectMake(x,y,width,height)];
// We add just using self NOT self.view because here, we are the view!
[self addSubview:newButton2];
}
return self;
}
So in this example we would have a view controller who'se view now contains 2 button! But the view structure is a tree:
mainView
/ \
newButton myView
\
newButton2
Let me know if you have any other questions!
Matt

UITableView partially hidden by UITabBar

I've got a UITabBarController which contains a UINavigationController. Within the visible UIViewController, I'm creating a UITableView programatically as follows:
self.voucherTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
self.voucherTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
However, the UITabBar is overlapping the UITableView.
When I output the height of the [[UIScreen mainScreen] applicationFrame], it returns 460.00 whereas it should be 367.00.
In Interface Builder, I'm using the 'Simulated Metrics' which automatically sets the height of the view to 367.00.
Is there something I'm missing, no matter what I try I can't see to get the 367.00 height that I need.
As a temp fix, I've set the frame of the UITableView manually, this isn't really ideal so it would be nice to work out why this isn't working:
self.voucherTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 367) style:UITableViewStylePlain];
You should use self.view.bounds rather than [[UIScreen mainScreen] applicationFrame] as the last one returns you the whole screen frame while self.view.bounds provides you with your view bounds wich seems what you are searching for.
You should add the UINavigationController instance to the UITabBarController and then add a table view controller to the rootViewController property of the UINavigationController instance which should make your life a lot easier.
As a simple example of this, create an empty window-based application (the templates make this a lot more confusing than it really is).
Add your UIViewController/UITableViewController subclasses to the project then use this code as a guide to setting up your project. This code is in your AppDelegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// create our table view controller that will display our store list
StoresViewController *storeListController = [[StoresViewController alloc] init];
// create the navigation controller that will hold our store list and detail view controllers and set the store list as the root view controller
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:storeListController];
[navController.tabBarItem setTitle:#"TableView"];
[navController.tabBarItem setImage:[UIImage imageNamed:#"cart.png"]];
// create our browser view controller
BrowserViewController *webBrowserController = [[BrowserViewController alloc] init];
[webBrowserController.tabBarItem setTitle:#"WebView"];
[webBrowserController.tabBarItem setImage:[UIImage imageNamed:#"web.png"]];
// add our view controllers to an array, which will retain them
NSArray *viewControllers = [NSArray arrayWithObjects:navController, webBrowserController, nil];
// release these since they are now retained
[navController release];
[storeListController release];
[webBrowserController release];
// add our array of controllers to the tab bar controller
UITabBarController *tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:viewControllers];
// set the tab bar controller as our root view controller
[self.window setRootViewController:tabBarController];
// we can release this now since the window is retaining it
[tabBarController release];
[self.window makeKeyAndVisible];
return YES;
}
In the code sample above the BrowserViewController is a subclass of UIViewController and the StoresViewController class is a subclass of UITableViewController. The UITabBarController and UINavigationController instances are created programmatically and added to the window.
By subclassing the UITableViewController class you avoid having to create a UITableView instance programmatically and get most everything you need out of the box.
When you need to push a detail view onto the UINavigationController instance's stack, you just have use something similar to this:
[self.navigationController pushViewController:YourDetailViewControllerInstance animated:YES];
This will add the detail view UIViewController subclass to the UINavigationController instance's view hierarchy for you and animate the transition.
Lots of controllers in this, but it's totally worth it and will avoid a lot of the problems you're experiencing as this method allows the views to manage resizing and take toolbars/navigation bars into account all by themselves.

Subviews not showing up in UIView class

I'm trying to lay out some images in code using addSubview, and they are not showing up.
I created a class (myUIView) that subclasses UIView, and then changed the class of the nib file in IB to be myUIView.
Then I put in the following code, but am still getting a blank grey screen.
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
[self setupSubviews];
}
return self;
}
- (void)setupSubviews
{
self.backgroundColor = [UIColor blackColor];
UIImageView *black = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"black.png"]];
black.center = self.center;
black.opaque = YES;
[self addSubview:black];
[black release];
}
yes, just implement initWithCoder.
initWithFrame is called when a UIView is created dynamically, from code.
a view that is loaded from a .nib file is always instantiated using initWithCoder, the coder takes care of reading the settings from the .nib file
i took the habit to do the initialization in a separate method, implementing both initWithCode and initWithFrame (and my own initialization methods when required)
try implementing initWithCoder: sometimes I've had trouble with IB and initWithFrame:
or at least add a logging call to see if your init method is executed