I have the following snippet of code:
- (void)loadView {
self.view = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 20.0, 40.0, 40.0)];
self.view.backgroundColor = [UIColor blackColor];
}
If I run the snippet in a project created using the 'view-based application' in the main view controller everything works as expected. However, If I run it in a project created using a 'navigation-based application' the label fills the screen (does not respect the sizes). How can I fix the behaviour for presenting when in a navigation controller? Thanks!
Navigation view controller will resize the view of the view controller to the display area, so the behavior you describe is normal and by-design. Generally, we use empty UIView at the top of hierarchy, and put the subviews inside, which is UILabel in your case.
Related
As I have told you many times, I'm new to iOS development.
I've learned something about views, view controllers, views hierarchy and stuff like that, and I'm thinking about one thing.
Assume that I want to create some app using some tab section, some table section, drawing canvas with context menu (quartz2D) etc, and I would like to have all these things visible at a time (static application) and can ensure some interaction between all these sections (changing something in the canvas results in some changes in the table, editing some values via context menu on canvas results in drawing a charts in tab pane and so on...).
The question is, if it is efficient to put all views on one screen and have some viewcontroller inside viewcontroller inside viewcontroller inside v...
How should I manage all particular views (with controllers) assuming that I have to present all of them at a time during entire application workflow?
/* PS. I'm new to StackOver as well, so let me know if such basic and not concrete questions like that one, are unwelcome */
You can't add ViewController inside a ViewController , you have one view Controller and on it's View you add SubViews Buttons, labels,...etc.
You can iterate on the subViews by get self.view.subViews
If you have a complex view, please make it as CustomView that inherits from UIView to make the code readable and well organized.
UIView *v1 = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 270, 400)];
v1.backgroundColor = [UIColor redColor];
UIView *innerView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 240, 370)];
innerView.backgroundColor = [UIColor greenColor];
[v1 addSubview:innerView];
[self.view addSubview:v1];
for (UIView *subview in self.view.subviews)
{
//You can get all subView object that You added
}
Apple's tab bar controller has a lot of limitations. One important limitation is that you can't modify the tab bar in a rejection safe mode. My tab bar has a simple sliding movements and it's multi row.
For those reasons I decided to build a TBVC from the beginning; everything seems to work correctly, but I'm really messing around with rotation. Every time that I change orientation main view frames are changed.
Here is my hierarchy from top to the container view:
-MainView--contains-->TabBarView+containerView
The containerView is the view used to contain views loaded from the other controllers.
Here is the -loadView method of my CustomTabBaViewController
- (void)loadView
{
UIView *theView=[[UIView alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
theView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
theView.backgroundColor=[UIColor greenColor];
containerOfControllersView=[[UIView alloc] initWithFrame:theView.bounds];
containerOfControllersView.backgroundColor=[UIColor blueColor];
containerOfControllersView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[theView addSubview:containerOfControllersView];
ideoloTabBar=[[IdeoloTabBar alloc]initWithNumberOfControllers:[controllers count]];
[theView addSubview:ideoloTabBar];
self.view=theView;
[theView release];
}
When I set a new view from another controller I use this method:
-(void)setCurrentViewWithView:(UIView*)theView{
if ([[self.containerOfControllersView subviews] count]>0) {
UIView *tagView=[self.containerOfControllersView viewWithTag:555];
tagView.tag=0;
[tagView removeFromSuperview];
}
theView.tag=555;
theView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
theView.frame=[[UIScreen mainScreen]applicationFrame];
[self.containerOfControllersView addSubview:theView];
[self.view bringSubviewToFront:ideoloTabBar];
}
As you can see the views from other view controllers are applied using the applicationFrame.
When I rotate the device happens something wrong, the mainview not only is resized according to the new orientation but also moved by 20px (status bar size) to the botton, thus leaving a gap between the status bar and the container view. Since I gave the mainview the screen bounds I can't understand with it should be moved.
UPDATE
I'm trying a different approach so I've modified the loadView like that:
- (void)loadView
{
[super loadView];
containerOfControllersView=[[UIView alloc] initWithFrame:self.view.bounds];
containerOfControllersView.backgroundColor=[UIColor blueColor];
containerOfControllersView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.view.backgroundColor=[UIColor greenColor];
[self.view addSubview:containerOfControllersView];
ideoloTabBar=[[IdeoloTabBar alloc]initWithNumberOfControllers:[controllers count]];
[self.view addSubview:ideoloTabBar];
}
And in the setCurrentViewWithView:(UIView*)theView I've modified the line with
theView.frame=self.view.bounds;
instead of using the applicationFrame.
NOW:
On iPhone when I try to load a modalView it cuts about 40px at the bottom
On iPad when I try to load a modalView it lefts 20px at the bottom, because 20px are under the status bar but wantsFullScreen is NO.
UPDATE 2
It seems that the presentModalViewController should be called from the root view controller. I will create a protocol and an abstract UIViewController subclass to implement it an load it correctly.
Any suggestion? work around?
I don't like the approach of creating an entirely custom TabBarController from scratch. I like to put a custom view on top of a real TabBar as a subview, and then pass all the button presses to the real TabBarController. This way you don't have to code a window manager yourself.
- (void)tabButtonPressed:(id)sender
{
UIButton *button = (UIButton *)sender;
if (button.tag == HomeButton)
self.tabBarController.selectedIndex = 0;
// etc.
}
This should also be rejection safe.
This has been addressed here: Application frame leaves blank line at the top
But, you could also specify your frame by subtracting 20 from y:
CGRect rect = [[UIScreen mainScreen] applicationFrame];
theView.frame = CGRectMake(rect.origin.x, rect.origin.y - 20, rect.size.width, rect.size.height);
It has been a while since I'm using my custom TabBarViewController with disappearing tabbar and it seems to work properly both on iPad and iPhone.
The main problem that I had was due to an incorrect assignment to the content view frame and probably to a wrong assumption that modalVC were loaded from the current view controller.
First point: the content view should use the bounds of the main view, here is a part of the loadView method of the Root View Controller:
[super loadView];
containerOfControllersView=[[UIView alloc] initWithFrame:self.view.bounds];
Second:before add as a subview a view of a view controller remark to it that its frame should have the same bounds of its new parent view.
theView.frame =self.view.bounds;
Third: modal view controllers should be loaded from the root view controller or the will never have correct size. That's why I've implemented a base abstract class for each view controllers that inherit a protocol that manage the presetation and dismissing of modal viewcontrollers.
Hope this helps someone else.
Andrea
I have an issue with a UINavigationBar and its y-offset. The bar is displayed without a UINavigationController as superview, yet that should not matter. In the viewController where the navigation bar appears the setup looks like this:
// Add Basic View
CGRect viewFrame = [[UIScreen mainScreen] applicationFrame];
UIView *myView = [[UIView alloc] initWithFrame:viewFrame];
myView.backgroundColor = [UIColor greenColor];
self.view = myView;
[myView release];
UINavigationBar *myBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
.... add some Stuff to the bar...
[self.view addSubview:myBar];
[myBar release];
As I add the navigationBar as a chield View to self.view I assumed that origin.y = 0 would mean that the bar should get directly displayed below the status bar. This works as expected if I start the app on my iPad, rotate it once (or more) and then drill down to the view that is described above. In this case the UINavigationBar is displayed properly. Yet if I start my app and directly drill down to the controller described above (without rotating the device before this particular controller appears) the navigation bar slides 20 points below the status bar. But as soon as I rotate the device then, the bar is fine again. I have checked the viewFrame.origin.y value and it is 20 points in both situations, hence I do not understand why in one case the bar just seems to ignore the origin.y value of its superview but does not in the other.
I am really confused about this, has anybody else ever experienced such an issue?
Thanks a lot for your help!
Ps. I have also tried it with a UIToolbar, the problem is the same.
Yes. My solution is to set the "Full screen on launch" flag to on in Interface Builder for the window in the MainWindow-iPad.xib file. Then design your views as if the 20 pixel status bar were always displayed, so in my root view, I have a toolbar that is positioned 20px below the top of the screen in the content view.
I've seen this question asked around before, and found an answer for how to do this in a simple view. But... when I go to a subsequent view pushed onto the view stack, manually setting the titleView doesn't work out. The titleView view gets pushed off to the right while the back button and its text take over the left half of the UI.
Is there a standard way to do this? I've noticed the Gowalla app apparently does it quite well. I've tried a multitude of approaches including categories, subclasses, etc and haven't had any luck.
Every UIViewController has it's own navigationItem, which (potentially) has a titleView. When you push and pop view controllers in a navigation control, the parts of the navigationItem are what you are seeing. If you wanted a custom title color, you could very easily do something like the following in each of your view controllers.
- (UINavigationItem *)navigationItem
{
UINavigationItem *navigationItem = [super navigationItem];
UILabel *customLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320.0f, 44.0f)];
customLabel.text = #"My Title";
customLabel.textColor = [UIColor purpleColor];
navigationItem.titleView = customLabel;
[customLabel release];
return navigationItem;
}
I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}