Edit 2: When I start the app without the status bar on top everything behaves as planned. With the status bar I couldn't get the views to act as I wanted. It looks as if the UINavigationController keeps resizing the content view by subtracting the 20 pixels of the status bar. I don't know.
I created a simple UINavigationController-based application. The root view in this navigation controller is a UITableView. At a certain time I want to slide in a 80 pixel high view from the bottom. The whole view on the top (the one that is controlled by the UINavigationController) should resize and get 80 pixel smaller to make room for the new bottom view.
This is basically the code I use to repositioning the views:
-(void)showTeaser {
float adHeight = 80;
[adView setFrame:CGRectMake(0.0,self.navigationController.view.bounds.size.height, 320.0, 80.0)];
[[[UIApplication sharedApplication] keyWindow] addSubview:adView];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[adView setAlpha:1.0];
[adView setFrame:CGRectMake(0.0,self.navigationController.view.bounds.size.height-adHeight, 320.0, 80.0)];
[self.navigationController.view setFrame:CGRectMake(0.0,0.0, 320.0, self.navigationController.view.bounds.size.height-adHeight)];
[self.view setFrame:CGRectMake(0.0, 0, 320.0, self.view.bounds.size.height-adHeight)];
[UIView commitAnimations]; }
I lowered the Navigationbar's alpha, set the UITableviewController's view to red. The new view is purple.
This is what happens. First screenshot initial state. Everything is looking normal. Second screenshot shows state after changing the frames. The view of the UITableviewController is always pushed 20 pixel under the Navigationbar. Also, if I try to add more views to the keywindow, they always end up 20 pixel higher than I expect. It almost looks like the keywindow (minus the navigation bar) is pushed up 20 pixel.
Edit 1: No matter to what size I resize the view, it's always 20 pixel.
Do I make a mistake by adding views to the keywindow at all? Shouldn't I do this?
alt text http://www.hans-schneider.de/iphone-seo/1.png alt text http://www.hans-schneider.de/iphone-seo/2.png
To solve this, I made the view of the UINavigationController a subview of a UIView, and manually set the bounds of the view for the `UINavigationController'.
//outerView is a UIView defined in the interface
outerView = [[UIView alloc] initWithFrame:CGRectMake( 0.0, 0.0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);];
//mainNavigationController is a UINavigationController defined in the interface
//rootViewController is a UIViewController (or inherited class) defined in the interface and instanced before this code
mainNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
//set the frame for the view of the navigation controller - 20 is due to the status bar
mainNavigationController.view.frame = CGRectMake( 0.0, 20.0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 20);
Then later, when I go to resize, I resize the parent 'UIView' rather than the 'UINavigationController' view.
//change the outer view's frame to resize everything
//adHeight is a float defined earlier
outerView.frame = CGRectMake( 0.0, 20.0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 20 - adHeight);
This works well in an animation sequence.
Edit 2011-05-12: I updated the outerView frame to fill the screen. This must be set to allow for touch events.
Have you tried using the transform property of your tableview instead of manually changing it's frame? It may work out better, since the frame depends on the origin, and you only want to change it's size.
Related
I have made a custom UIView which is shown when the user hits a button in the navigationbar. I make my view's in code. In my loadview I set the autoresizing masks and the view loads correct on screen. However the UIView which is shown when the user taps the button does not resize even when I have set the autoresizing masks.
UIView *blackView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 416.0)];
blackView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Do I need to use self.view.frame.size.width and self.view.frame.size.height instead? And if I do why? Does not resizing masks work outside of loadView?
Thank you for your time:)
the autoresizingMask affects how a view will behave when its superviews frame changes. if all you are doing is showing theblackViewwhen you tap a button, thenblackView` will have whatever frame you initially set for it.
If this isn't enough info, please post some more code around how you are configuring and displaying blackView and it's superview and explain more about what situations you are expecting blackView to resize in. Rotation is one of them, if that's what you're concerned with.
First things first, I hope you've done this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Let's say the view that needs resizing is: view2
The view that has view2 as a subview is: view1
While creating view1 you would declare it as:
view1 = [[UIView alloc] init];
[view1 setNeedsLayout];
Now in view1's .m file you need to overload the layoutSubviews method as shown:
- (void)layoutSubviews
{
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
In case view1 is a view controller's view, you need to do that same thing as above in the willRotate method as shown
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
This is a tried and tested method that I use to handle orientation changes.
I've managed to manually detect rotation and set my own rotation effects between two UIViewControllers. When in the first, rotating to landscape prompts the second to be pushed on with a manual animation. The status bar moves to the correct orientation. However, the view loaded by the second UIViewController is not in the position I expected it. Instead there is margin on the left where the status bar previously was and margin/space at the bottom that I was expecting to be filled by the view.
(Click to enlarge. The orange box is simply to reference where 0,0 is in the rotated UIView, CustomView)
The code I'm using in my view controller to do the rotation is:
-(void)loadView {
CustomView *customView = [[CustomView alloc] initWithFrame:CGRectMake(0, 20, 480, 300)];
self.view = customView;
CGAffineTransform rotate = CGAffineTransformMakeRotation(degreesToRadian(90));
[self.view setTransform:rotate];
[customView release];
}
Any help would be much appreciated!
EDIT Managed to solve after a variety of trial and error approaches - answer provided below. Perhaps there's a more elegant/obvious solution though - if so feel free to provide!
try these on the viewcontrollers. Solved the 20 pixel(status bar height) clip issue when I was getting fullscreen subviews to show up.
[viewcontroller1.view setCenter:CGPointMake(viewcontroller1.view.center.x, viewcontroller1.view.center.y-20)];
and/or
[viewcontroller2.view setCenter:CGPointMake(viewcontroller2.view.center.x-20, viewcontroller2.view.center.y)];
I managed to solve this by setting the View's bounds after the transformation:
self.view.bounds = CGRectMake(20, -20, 480, 300);
I have a .nib file created in Interface Builder with 1 UIView item, but when I initialize it programmatically, the view doesn't cover up the entire screen, leaving a small portion of the view beneath it visible.
It looks like the Y coordinate is at some negative position instead of at 0. I don't have a Navigation bar configured for the view but if I were to set it, the bar doesn't get shown.
I'm creating the view like this inside the appDelegate class:
MyCustomController *controller = [[MyCustomController alloc] initWithNibName:#"MyCustomView" bundle:nil];
[window addSubview:[controller view]];
Settings in Interface Builder for the view:
Orientation: Portrait
Status Bar: Gray
Top Bar: unspecified
Bottom Bar: unspecified
View mode: "Scale To Fill"
View Size/Position: Set to "Layout" and the X/Y, Width/Height are readonly with 0,0,320,460
I've printed out the view's frame and [[UIScreen mainScreen] bounds] on 'viewWillAppear':
view.frame on viewWillAppear - x: 0.0, 0.0, 320.0, 460.0
bounds - x: 0.0, 0.0, 320.0, 480.0
I'm not sure why the view is getting shifted - what's the best place to read just the frame size once the view is loaded by the 'initWithNibName' method? That seems to be the workaround I can think of.
I wrapped my controller with a UINavigationController and the problem went away.
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 am writing an iPhone app with a tab bar and navigation bar. At a certain point, I am pushing an instance of my DetailsViewController class onto the navigation controller stack to show it.
This controller creates its view hierarchy in code: the controller's view property is set to a UIScrollView, which contains a plain UIView (let's call it "contentView") sized to hold all the content to be shown. At the bottom of this contentView, I have four UIButtons.
Now when I run the app (in the simulator at present), and scroll to the bottom of the view, the top two buttons respond to touches; the third responds to touches only in the top portion of it, and the lower button doesn't respond to touches at all. By clicking in various parts of the third button, it appears that the lower 93 pixels of the scroll view is not passing touch events through to its subviews.
93 is suspicious: it's also the combined height of the tab bar (49 pixels) and navigation bar (44 pixels). Yet the navigation bar and tab bar are outside the scroll view. Any suggestions why this might be happening?
Here's the code in question:
- (void)loadView
{
CGRect frame = [[UIScreen mainScreen] applicationFrame];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
scrollView.delegate = self;
self.view = scrollView;
UIView *contentView = [[UIView alloc] initWithFrame:scrollView.bounds];
contentView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[scrollView addSubview:contentView];
CGSize viewSize = contentView.bounds.size;
CGSize size;
CGFloat y = 0;
/* Snip creating various labels and image views */
/* Actions view creates and lays out the four buttons; its sizeThatFits:
** method returns the frame size to contain the buttons */
actionsView = [[PropertyDetailActionsView alloc] initWithFrame:CGRectZero];
actionsView.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth);
actionsView.delegate = self;
size = [actionsView sizeThatFits:viewSize];
actionsView.frame = CGRectMake(0, y, size.width, size.height);
[contentView addSubview:actionsView];
y += size.height;
[contentView setFrame:CGRectMake(0, 0, viewSize.width, y)];
scrollView.contentSize = contentView.frame.size;
[contentView release];
[scrollView release];
}
As I suggested on Twitter yesterday, it may have something to do with the flexible bottom margin set to the actionsView.
That suggestion did not resolve the problem, yet it lead to the right direction. By removing the flexible height of the contentView the problem has been fixed.
So if anyone out there is having similar problems, try to play with your autoresizingMasks.
also make sure all your content views are the height that covers the bottom button.
I make each view a different color to see them.