UIViewController - iphone

My question is: i don't understand that we can create a ViewController with a nib file and we can create it without a nib file. i mean that : for example can anyone explan me the template, Navigation based application how it work, what is the first object instanciated ?
thanks for your answers

The app's Info.plist file contains a property called "Main nib file base name" (NSMainNibFile). The nib file that is set here ("MainWindow.xib" by default) controls what will be loaded at startup.
If you don't have that set, and you want to launch an application without a default nib file, you need to pass in the name of your app delegate in your main.m file.
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
http://blog.hplogsdon.com/ios-applications-without-any-nib-files/

the only method to instantiate a UIViewControler is:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
that mean you "normally" ask it to load a .nib file...
but you can also pass "nil" to both parameters:
myUIViewController = [[MyUIViewController alloc] initWithNibName:nil bundle:nil];
...if you want to load it directly and manage it by yourself. Generally you crate a subClass of UIViewController (MyUIViewController in my sample) and in its #implementation you implement the method loadView
where you need to create the view of your class
- (void)loadView{
UIView *aUIView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320, 480)];
self.view = aUIView;
aUIView.backgroundColor = [UIColor colorWithRed:.2 green:.3 blue:.5 alpha:1];
// aUIView... other properties to set if needed...
[aUIView release];
}
this way you can manage it all without a ".nib file", adding all objects and subView only via code...

Related

Accessing UIView from UIViewController

I have added a UIView to a UIViewController using storyboards (See Image Below)
The problem is I cannot access anything from the UIView. I believe the issue is because when I init the class which UIView uses, I am creating a new instance of VASettingsView rather than using the one which has been added to the UIView frame using storyboards:
VARendererViewController.m
-(void)viewDidLoad{
...
// Initalise settings subview
self.settingsInit = [[VASettingsView alloc] init];
self.settingsInit.delegate = self;
....
}
-(void)update{
int test = [[self settingsInit] getTest];
NSLog(#"%d", test);
}
Returns 0, Should return '12345'
Any suggestions?
You are correct that you should not need to create a new VASettingsView in -viewDidLoad. Is settingsInit declared as an IBOutlet? You should be able to make a connection from the VASettingsView in Interface Builder to the outlet in your view controller.
-viewDidLoad is the method that is called after your nib is loaded; at that point all of the IBOutlets that were connected in your nib will be available for you to use. It is not necessary to create and set those properties.

Why is adding a view crashing the program?

In an attempt to figure out how to integrate code from one program into another I have dumped 3 classes into an empty open GLES game project. The classes are ContentController, PhoneContentController and MyViewController from the PageControl apple sample.
I have then taken this initialisation code form the PageControl app delegate file and put it into the game project appDelegate.
contentController = [[PhoneContentController alloc] init];
[self.window addSubview:contentController.view];
with the necessary synthesize / protocols / includes and declarations where they are in PageControl.
I get a SIGABRT error on this line in main:
return UIApplicationMain(argc, argv, nil, NSStringFromClass([IntegrationTestAppDelegate class]));
I added a general breakpoint and it points to this line in MyViewController.m
newsItem = [[UITextView alloc] initWithFrame:self.view.frame];
I'm guessing this has something to do with self.view.frame ....? maybe? So, is there some hierarchy type issue at work here?
EDIT - on further inspection it does seem to have something to do with views... although I don't quite know what. Could this have something to do with a foreign nib file?
EDIT - more detail: the newsItem is called from init of myViewController which is called from a method within phone content controller, which in turn is called with the following line in appdelegate:
contentController = [[PhoneContentController alloc] init];
At what point is:
newsItem = [[UITextView alloc] initWithFrame:self.view.frame];
being run? In particular, is it after nib loading is complete? If not, and self.view was set in Interface Builder, then self.view is going be nil, and self.view.frame is going to be garbage. (-initWithFrame: expects a CGRect struct, so in this case a nil pointer is wholly unacceptable, unlike in methods that expect Objective-C objects.)

Load NIB from variable

I'm trying to load a NIB based on a variable I get from my settings file. This is the code:
//select the right nib name
NSString *nibVar = [nibs objectForKey:#"controller"];
// create the view controller from the selected nib name
UIViewController *aController = [[UIViewController alloc] initWithNibName:nibVar bundle:nil];
aController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:aController animated:YES];
[aController release];
This unfortunately does not work.
Any ideas here?
Thanks
You cannot instantiate "UIViewController" with arbitrary NIBs, you have to instantiate "[whatever your custom view controller class is]" with the NIB for that class.
It's crashing because it's trying to access properties that don't exist in UIViewController.
If you want to do this kind of dynamic view-controller loading, you need to do a bit more work, and use the special Class class method that lets you instantiate an object using a string for the class name, instead of hard-coded.
Sometehing like:
Class viewControllerClass = NSClassFromString( nibVar );
UIViewController* aController = (UIViewController*) [[viewControllerClass alloc] initWithNibName:nibVar bundle:nil];
Make sure the NIB name is correct and does not include the xib extension. It is also case sensitive.

Bug with UIView frame property?

I am initializing a new UIViewCOntroller object.
then attempting to set its view's position of stage but I am having some trouble.
here is the code I am using
Note: this code is placed in the application main UIViewController's viewDidLoad method
UIViewController * cont = [[UIViewController alloc] init];
cont.view.backgroundColor = [UIColor redColor];
CGRect rect = CGRectMake(100, 0, 320, 480);
cont.view.frame = rect;
this code is still positioning the subview at (0,0) instead of (100,0)
However, if I introduce a decimal, such as using 320.01 (for the width value) or 480.01 (for the height value). The view would be positioned correctly.
It seems that if I use a size with an exact width:320.0 height: 480.0,
the origin will always be set to (0,0) !!!
This is a bit strange. I was hoping that someone could explain why this is happening, and possibly how it may be resolved.
Cheers ....
NSLog the value of cont.view and I think you will find it to be nil, which explains why nothing's happening. This is not the normal way to create a UIViewController -- it's not wrong to create one programmatically, but 99.99% of the time UIViewController subclasses are created with the main UIView in a .xib file. A freshly created UIViewController object has a nil "view" member, so you've got to initialize it somehow, either by loading a .xib:
MyViewController *vc = [[[MyViewController alloc] initWithNibName#"MyViewController" bundle:nil] autorelease];
or manually creating the view:
MyViewController *vc = [[[MyViewController alloc] init] autorelease];
UIView *theView = [[[UIView alloc] initWithFrame:viewframe] autorelease];
vc.view = theView;
Then you can move the view's frame to your heart's content, but moving the base view of a view controller is usually not what you want to do, you want to create sub-views and move those around.
[[UIViewController alloc]init] is wrong. The designated initializer for UIViewController is initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle. Even that does not necessarily initialize the view outlet to an actual UIView immediately. You need to subclass UIViewController and perform your customisations in the viewDidLoad method of that subclass.
In the interim is likely that view is nil so you can try setting whatever properties of it you like without anything ever happening.
I think you should be able to use
-(void) loadView {
[super loadView];
//create your views programmatically here
}
in order to create your viewController programmatically and avoid the IB. Normally the IB calls this method for you when your 'view' property is nil, however if you're avoid the IB make sure to include the above method so your view property is not nil.

UIViewController: set self.view to my view or add my view as subview?

I have a question about UIViewController's subview, I created a UIView subclass MainView, which has the exact size of the screen, I wonder which is a better way of adding MainView, consider the following factors:
1 As MainView has same size as the whole screen, the MainView itself may have subviews, but there is no views at the save level as MainView(ie I don't need to add other subviews to self.view).
2 If I use self.view = mainView, do I put the code in loadView(as the viewDidLoad method means the view(self.view) is already loaded)? I see the loadView method is commented out by default, if I add the code to this method, what other code do I need to put together(e.g. initialize other aspects of the application)?
3 If I add mainView via [self addSubview:mainView], are there actually two off screen buffer? One for self.view, one for mainView, both has same size as the screen and one is layered on top of the other(so it wastes memory)?
Thanks a lot!
I'm not sure I completely understand what you're asking, but I'll try to answer a few of the questions you have.
First of all, if you have multiple UIViews on the screen they are all loaded into memory. You have to do -removeFromSuperview and release them to get the memory back.
You can assign your UIView as the UIViewController's view. For example:
MainView *mainView = [[MainView alloc] initWithFrame:CGRectMake(320.0, 480.0)];
self.view = mainView;
[mainView release]; //since the .view property is a retained property
in that case, you have have the view's initialization code in the -init method. Just redefine it like:
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
//initializations
}
return self;
}
You must implement loadView if you did initialize your view controller with a NIB.
UIViewController takes care of sizing its "main" view appropriately. This is all you need to do:
- (void)loadView
{
UIView* mainView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
self.view = mainView;
}
I'd solve all of this by doing it in a xib! If you create a UIView in your xib, you can then change it's class (when you select the UIView there should be a text field in the Class Identity section of the Identity inspector* - type 'MainView' here!)
Then, create your view controller by calling
myViewController = [[MainViewController alloc] initWithNibName:#"MyNibName" bundle:nil];
That should solve your problems; it's the main subview of your view controller (directly accessable from self.view) and you don't need to worry about memory usage, there's only one view :)
Sam
NB * Click tools -> Identity Inspector. I didn't know it was called this until I had to write this answer!
Yes, the first code-snippet shown above is the "standard" approach, AFAIK, when not using (evil!) NIB files -- i.e. when alloc'ing your view in-code, via loadView.
Note it seems one can also get away with the following, instead of hard-coding the screen-rect size:
UIView *myView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.view = myView;
[myView release];
Note you definitely want to do the [myView release] call since, indeed, as pointed out above, self.view (for UIView) is a retained property.
Cheers, -dk
Perhaps the most important thing to do is make sure you have the following:
self.view.userInteractionEnabled = YES;
While it might not be required all of the time, it fixes the issue where self.view is unresponsive. This issue pops up occasionally.