I've been trying to understand View Controllers and Views and even after watching some of the classes on iTunesU, I'm still having some trouble implementing them programmatically. I'm hoping someone can clarify a bit.
So I'm trying to create a UIViewController which in turn creates its view.
The program is broken up in the following classes:
ProgramNameAppDelegate.h and .m
ApplicationRootViewController.h and .m
From the AppDelegate, I create the UIWindow and UIViewController. Partial code goes like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
_window = [ [UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if (!_window)
{
[self release];
return NO;
}
_rootViewController = [ [ApplicationRootViewController alloc] init];
if (!_rootViewController)
{
NSLog(#"No _rootViewController");
[self release];
return NO;
}
[_window addSubview:[_rootViewController view]];
[_window makeKeyAndVisible];
return YES;
}
In the ApplicationRootViewController I call init. My UIView is created in loadView as such:
- (void)loadView
{
NSLog(#"In loadView");
[super loadView];
CGRect _frame = [[UIScreen mainScreen] applicationFrame];
UIView* _rootView = [[UIView alloc] initWithFrame:_frame];
[_rootView setBackgroundColor:[UIColor redColor]];
self.view = _rootView;
return;
}
The problem I'm having is apparently the program is creating the view, however, it is never displaying the view that I created until the app resigns active. Once I go out of the app and come back in, the view is there. I have tried several other things, but it always behaves the same.
I would eventually like to for the controller to create the view from a subclassed UIView.h and .m file.
Thanks,
Kevin
From the docs:
Your custom implementation of loadView
method should not call super.
So, get rid of [super loadview] and it should work;)
Also, if you want to use a custom view (Subclass of UIView). Alloc/Init using initWithFrame: and when referring self.view from uiviewcontroller you will have to cast it like so:
[(MyView *)self.view myMethod];
As simple as that ;)
EDIT:
Suppose you make a class like this:
//MyView.h
#interface MyView : UIView{
...
}
- (void) doSomething:(NSString *)string;
#end
//MyView.m
#import MyView.h
#implementation MyView
... write your implementation here
#end
then in the UIViewController,
in loadView
do:
//don't forget to #import "MyView.h"
-(void) loadView{
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(...)];
self.view = (UIView *)myView;
[myView release];
}
then when referring your view somewhere else in the controller:
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[(MyView *)self.view doSomething:#"something"];//1
[self.view setBackgroundColor:[UIColor greenColor]];//2
}
In //1 you should cast because your are calling doSomething: method (or it could be a property as well), which is declared/defined in MyView and not in UIView. If you don't cast you will get a warning but it will work.
In //2 you don't need to cast since setBackgroundColor: is a method defined in UIView class ;)
Objective-C is very flexible and will allow to cast many things, so you have to be careful because casting is like telling the compiler: "trust me, is not a UIView, is MyVIew" and the compiler will obey you. But if you were wrong your app will crash when attempting to call doSomething: because it does not exist in UIView.
Related
I want to create a different class with a view and call that class on screen.
When I run the app, the view does not appear. If I delete that structure and create the button on the main file, it works fine. When I put it on a different class, it does not work.
MyView.h
#import <UIKit/UIKit.h>
#interface viewHome : UIViewController
-(UIView*) myHome;
#end
MyView.m (Creating a button for test)
#import "viewHome.h"
#implementation viewHome
-(UIView*) myHome {
UIView * myScreen = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
myScreen.backgroundColor = [UIColor whiteColor];
UIButton * myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
myButton.frame = CGRectMake(100,100,100,44);
[myButton setTitle:#"Login" forState:UIControlStateNormal];
[myScreen addSubview:myButton];
return myScreen;
}
#end
viewController.m
[...]
- (void)viewDidLoad
{
[super viewDidLoad];
viewHome * fncScreen;
UIView * homeScreen = [fncScreen myHome];
[self.view addSubview:homeScreen];
}
Thanks
Its a bad practice to add one viewController's view as a subview to an another viewcontroller. (unless you are using certain classes/methods that let you have childViewControllers).
Its exactly for this reason, your view doesn't appear in one case, and appears in the other case.
try adding the -(UIView*) myHome method in your viewController.m, and do a
[self.view addsubview: [self myHome]];
here's a good SO post about Nested UIViewControllers:
Is it wise to "nest" UIViewControllers inside other UIViewControllers like you would UIViews?
You could call the method [fncScreen myHome] after allocation your view homeScreen.
like this -
UIView *homeScreen = [[UIView alloc] init];
homeScreen = [funScreen myHome];
[self.view addSubView: homeScreen];
[homeScreen release];
Hope this will help you.
One obvious mistake I see in your code is this line:
viewHome * fncScreen;
OK, that's a pointer declaration, but you didn't actually create the object.
You need something like this:
viewHome * fncScreen = [[viewHome alloc] init];
Then you can call methods on such initialized object.
am creating a application which is support all type of orientation, i added one UIView on the UIWindow.but on rotating the device the view which is added on the Window is not rotating. the view always showing default (Portrait). please help me to fix this problem ...
Thanks in advance
You need to add a UIViewController on your UIWindow.
UIView doesn't handle rotations, UIViewController does.
So, all you need is to create a UIViewController, which implements shouldAutorotateToInterfaceOrientation and sets this controller as a rootViewController to your UIWindow
Something like that:
- (void) makeWindow
{
UIViewController * vc = [[[MyViewController alloc] init] autorelease];
UIWindow * window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setRootViewController:vc];
[window makeKeyAndVisible];
}
#interface MyViewController : UIViewController
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
//your view initialization here
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
#end
I'm sure this is some stupid mistake, but i'm trying for the past hour to remove a subview from my superview without any success.
On my first view i'm having
UIViewController *helpView = [[[UIViewController alloc] initWithNibName:#"HelpView" bundle:nil] autorelease];
[self.view addSubview:helpView.view];
And then inside helpView i have a button which is connected to an IBAction called "closeHelp" which just does the following:
- (IBAction) closeHelp{
[self.view removeFromSuperview];
}
But this causes my app to crash with EXC_BAS_ACCESS for some weird reason, even those this is inside the HelpView, meaning self.view should be pointed to the correct subview..
Would appreciate your help
Thank you.
Shai.
As Andreas answered, you are trying to remove self.view from its super/parent view.
You basically need to remove the helpView from its parent view.
so it should be
- (IBAction) closeHelp{
[helpView removeFromSuperview];
}
But we dont know what is "helpView" in the above method. As we dont have any handle for it.
So our code should finally look like this.
#define HELP_VIEW_TAG 101 // Give tag of your choice
HelpView *helpView = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
helpView.view.tag = HELP_VIEW_TAG;
[self.view addSubview:helpView.view];
[helpView release];
- (IBAction) closeHelp{
UIView *helpView = [self.view viewWithTag:HELP_VIEW_TAG];
[helpView removeFromSuperview];
}
The self.view does not point to your subview but the root view which your uiviewcontroller manages. You should probably remove only the last object in the subview stack, not the whole view, because now you are removing the whole help view.
Anyway, why do you not present the viewcontroller modally instead of doing this?
[self presentModalViewController:helpView animated:NO/YES];
helpView. modalTransitionStyle = //One of the constants below
UIModalTransitionStyleCoverVertical
UIModalTransitionStyleFlipHorizontal
UIModalTransitionStyleCrossDissolve
UIModalTransitionStylePartialCurl
Usually I am writing self.modalTransitionStyle = // One of the constants
in the viewcontroller which will be presented modally, instead of spreading the code.
You are initializing helpView as a UIViewController.
Make sure you have #import "HelpView.h" (or whatever the helpView .h file is called) in the .h file of the view controller where you are initializing it.
Then, use this code:
HelpView *helpView = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
[self.view addSubview:helpView.view];
That should fix it.
The easiest solution for me eventually was to just define my XIB's file owner as the same class as the parent controller, meaning the parent controller would control both the parent and the subview, which just makes a lot easier. :)
Declare the help view on calss level.
in.h file
#class HelpView;
..
#interface
{
HelpView *helpView;
}
#property(nonatomic,retain)HelpView* helpView;
In.m file
#import "HelpView"
#synthensize helpView;
now add this Code where you want
helpView = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
helpView.view.tag = HELP_VIEW_TAG;
[self.view addSubview:helpView.view];
- (IBAction) closeHelp{
//UIView *helpView = [self.view viewWithTag:HELP_VIEW_TAG];
[helpView removeFromSuperview];
}
-(void)dealloc
{
[helpView release];
}
I have a viewcontroller which contains an instance variable containing a dictionary object with a bunch of data. The view is fairly complex and contains several subviews that i instantiate and embed from seperate view files(To avoid having a thousand lines of UI code in the actual viewcontroller) - But how do these subviews, which exists in their own files, get access to my dictionary object from the viewcontroller?
So when im editing the DescriptionView.m file - How do i get access to the contents of the locationData dictionary object from the ViewController?
Hope you understand what i mean.
Here's a snippet from the ViewController:
CaseViewController.h
#import "DescriptionView.h"
#interface CaseViewController : UIViewController {
NSDictionary *locationData;
DescriptionView *descriptionView;
}
#property (nonatomic, retain) NSDictionary *locationData;
#property (nonatomic, retain) DescriptionView *descriptionView;
#end
CaseViewController.m
- (void)loadView {
UIView *view = [[UIView alloc] init];
descriptionView = [[DescriptionView alloc] initWithFrame:CGRectMake(0, 130, 320, 237)];
descriptionView.hidden = NO;
[view addSubview:descriptionView];
self.view = view;
[view release];
}
Ideally you should never access any properties of viewcontroller from the view.
The main idea of MVC architecture is that viewcontroller tells it's views what to render and not vise versa.
So you just have to provide all the data that your view needs for rendering during it's initialization:
- (void)loadView {
UIView *view = [[UIView alloc] init];
descriptionView = [[DescriptionView alloc] initWithFrame:CGRectMake(0, 130, 320, 237) paramDict: self.locationData]; descriptionView.hidden = NO;
[view addSubview:descriptionView];
[descriptionView release]; // BTW add this line here (or in dealloc) or you'll have a leak
self.view = view; [view release];
}
If you need to update your view dynamically, then you should add some methods to your view and call them from viewcolnroller.
E.g.:
DescriptionView.m:
-(void) updateWithDict:(NSDictionary*) udict;
If you need to perform some actions when some button in DescriptionView is pressed (or any other user interaction) a good idea would be declaring a protocol like DescriptionViewDelegate (or smth like that):
-(void) descriptionViewButton1Pressed:(DescriptionView*) dview;
-(void) descriptionViewButton2Pressed:(DescriptionView*) dview;
then make your CaseViewController a delegate and implement that methods there.
The simpliest way to have a reference to its viewcontroller from a view is to extend UIView:
#interface MyView: UIView {
UIViewController *mViewController;
}
Then in loadView
MyView *view = [[MyView alloc] init];
view.mViewController = self;
here is my code , i am trying to get from one view to another without any memory leaks.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
firstviewcontroller *first = [[firstviewcontroller alloc] init];
[window addSubview:first.view];
[self.window makeKeyAndVisible];
return YES;
}
-(IBAction)gotosecondview:(id)sender
{
secondviewcontroller *second = [[secondviewcontroller alloc] init];
[self.view addSubview:second.view];
[second release];
}
-(IBAction)gotofirstview:(id)sender
{
[self.view removeFromSuperview];
}
to make the above code work without crashing , all i have to do is remove [second release].
if I remove it I get memory errors (build and analyze) . how can i solve this problem. and i dont want to use [self.navigationController pushViewController:second animated:YES];
all i am trying to do i navigating from one view to another and vice versa WITHOUT using navigation controller. my firstviewcontroller and secondviewcontroller are of type UIViewController.
Thanks in advance.
You need to keep the current view controller alive while its view is showing (so it can process the user input, etc.).
In your code, you can achieve that in several ways:
Keep an instance of firstviewcontroller and secondviewcontroller as instance variables, and release them on the dealloc method.
Keep an instance variable with the currently in use UIViewController and release it when you switch to another view.
The code for the second option would look something like this:
#interface
UIViewController *currentViewController;
#end
#implementation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
firstviewcontroller *first = [[[firstviewcontroller alloc] init] autorelease];
[self switchToViewController:first];
[self.window makeKeyAndVisible];
return YES;
}
- (void)switchToViewController:(UIViewController *)aViewController {
[currentViewController.view removeFromSuperview];
[currentViewController release];
currentViewController = [aViewController retain];
[self.window addSubview:currentViewController.view];
}
-(IBAction)gotosecondview:(id)sender {
[self switchToViewController:[[[secondviewcontroller alloc] init] autorelease]];
}
#end
Here, all the logic for maintaining a single UIViewController alive lies in the switchToViewController method, which also handles the logic for switching from one view to another. As an added bonus, you can quickly add support for animations by adding a couple of lines in switchToViewController.
You can not release view in the call.
There is only one thing you can do in such conditions. use Autorelease,
The reason [second release] is crashing your code is likely because you're releasing your view controller which in turn releases the second view. The iPhone cookbook has some sample code on switching/swapping views if that's all that you're trying to accomplish. Here's the link. Hope this helps!
link text