I have a navigation-based application where it is possible for the user to hide the status bar. This is remembered in a defaults setting.
It is mostly working, with the one exception that if the app is loaded (from scratch, not returned to after going back to the home screen) when the status bar should be visible, when it is toggled to invisible, the navigation bar does not move up to fill the screen. Toggling the setting after that moves the navigation bar up and down correctly, but still with the extra status bar-sized gap between the navigation bar and the top of the screen, or the status bar. If I return to the home screen and re-enter the application, this corrects itself. I therefore assume there is some method being called on the uinavigationcontroller upon return to the application that I need to call after my toggling of the status bar?
I have tried (I think) all combinations of the wantsfullscreenlayout property, I was setting it in the method below but it made no difference, so I ended up setting it (on the navigationcontroller) to NO in the nib.
Here is the code which toggles the status bar. This is in my application delegate, which has the navigationcontroller and window set up as outlets as per the template application.
if ([UIApplication sharedApplication].statusBarHidden != hideStatusBar)
{
[[UIApplication sharedApplication] setStatusBarHidden:hideStatusBar withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:0.25 animations:^{
window.frame = [[UIScreen mainScreen] applicationFrame];
}];
}
else
{
window.frame = [[UIScreen mainScreen] applicationFrame];
}
Thanks for your help.
UPDATE
It seems, via NSLogging, that the problem lies in the frame of the UINavigationBar. So I have added the following code, which works and animates but I am not happy with! I don't feel this can be the "correct" way to do this. In most cases the extra code does nothing since the frame is already at (0,0), but in the one situation where it is incorrect, this gives the right result.
[navigationController.view setNeedsLayout];
CGRect navBarFrame;
UINavigationBar *navBar = nil;
for (UIView *subView in navigationController.view.subviews)
{
if ([subView isMemberOfClass:[UINavigationBar class]])
{
navBar = (UINavigationBar *)subView;
navBarFrame = navBar.frame;
navBarFrame.origin = CGPointMake(0,0);
break;
}
}
if ([UIApplication sharedApplication].statusBarHidden != hideStatusBar)
{
[[UIApplication sharedApplication] setStatusBarHidden:hideStatusBar withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:0.25 animations:^{
window.frame = [[UIScreen mainScreen] applicationFrame];
navBar.frame = navBarFrame;
}];
}
else
{
window.frame = [[UIScreen mainScreen] applicationFrame];
}
The window always underlaps the status bar, so you shouldn't try to resize it.
Instead, resize the view of your window's root view controller to [[UIScreen mainScreen] applicationFrame].
the problem lies in the frame of the UINavigationBar. So I have added the following code, which works and animates but I am not happy with! I don't feel this can be the "correct" way to do this. In most cases the extra code does nothing since the frame is already at (0,0), but in the one situation where it is incorrect, this gives the right result.
[navigationController.view setNeedsLayout];
CGRect navBarFrame;
UINavigationBar *navBar = nil;
for (UIView *subView in navigationController.view.subviews)
{
if ([subView isMemberOfClass:[UINavigationBar class]])
{
navBar = (UINavigationBar *)subView;
navBarFrame = navBar.frame;
navBarFrame.origin = CGPointMake(0,0);
break;
}
}
if ([UIApplication sharedApplication].statusBarHidden != hideStatusBar)
{
[[UIApplication sharedApplication] setStatusBarHidden:hideStatusBar withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:0.25 animations:^{
window.frame = [[UIScreen mainScreen] applicationFrame];
navBar.frame = navBarFrame;
}];
}
else
{
window.frame = [[UIScreen mainScreen] applicationFrame];
}
As far as I know if you hide the status bar your views automatically occupy the new space and you don't have to change the window frame manually. What happens if you try just this
if ([UIApplication sharedApplication].statusBarHidden != hideStatusBar)
{
[[UIApplication sharedApplication] setStatusBarHidden:hideStatusBar withAnimation:UIStatusBarAnimationSlide];
}
This is what solved it for me
Just a View:
[self.view setFrame: [self.view bounds]];
A view with a scroll view inside
[self.view setFrame: [self.view bounds]];
[self.theScroller setFrame: [self.view bounds]];
"theScroller is the name of my scrollview
Related
I am developing an app, and I want it can be used on iPhone 4 and 5, as the iPhone 5 had changed its size to 4-inch, so I want my app can be supported 4-inch, but in Xcode4.3 there is no autolayout, because of this , I wonder if there is anyway to get the display screen size and change it to 4-inch, thank you very much!
You can get the screen size by using this code:
CGRect screenBounds = [[UIScreen mainScreen] bounds];
UIViewController *ViewController;
[self loadall];
if (screenBounds.size.height == 568) {
NSLog(#"User is using an iPhone 5+");
BOOL isUsingiPhone5 = YES;
}
else{
NSLog(#"User is using an iPhone 4s-");
BOOL isUsingiPhone5 = NO;
}
then you could move things accordingly, with CGRect heres an example of moving an ad banner view as a result of the screen size:
-(void)bannerViewDidLoadAd:(ADBannerView *)banner {
CGRect bannerFrame = banner.frame;
CGRect screenBounds = [[UIScreen mainScreen] bounds];
UIViewController *ViewController;
if (screenBounds.size.height == 568) {
//iPhone5+
bannerFrame.origin.x = 0;
bannerFrame.origin.y = 518;
banner.frame = bannerFrame;
}
else{
//iPhone4s-
}
NSLog(#"Showing ad, internet connection found.");
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
[banner setAlpha:1];
[UIView commitAnimations];
}
else{
[banner setAlpha:0];
}
Make sure to use ViewController as the UIViewController variable, even if your default view controller is named something like RootViewController.
Hope this helps. Feel free to comment if you have any questions!
I have been doing pretty much all of my User Interface programmatically with slight alterations being performed in Interface Builder.. but 99% of all the UI is exclusively done in code, because I feel there is a certain amount of flexibility gained by doing it this way.
However I am now having issues dealing with the rotation of the device, as I have several UIViews being added as subviews I am faced with a rotational problem as this is how I declare the views generally
htmlTest.webViewTest.frame = CGRectMake(4.0, 4.0, 312.0, 363.0);
and because of this fixed CGRectMake when the device is rotated the view stays the same size and dosent fit the orientation of the view properly.
So I have worked on a solution which is in my opinion horrible.. There are a couple of views that I animate in and users can select options from them then I animate them out.. but they need to be able to handle loading in either portrait or landscape and then if while they are loaded they need to be able to handle a rotation from either orientation to the other.
This is how I have done one of the views.
#pragma createAwesomeJumpBar
- (void)jumpBarButtonPosition:(int)changeView
{
// ChangeView is used to check if the this method is being called from a device rotation or from a button press (0, being rotation and 1, being tabbarButton touch
// if tabbar selected
if (changeView == 1) {
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait)
{
if (![jumpBarContainerPortrait superview]) {
// load portrait view
jumpBarContainerPortrait = [[UIView alloc] initWithFrame:CGRectMake(0.0, 480.0, 320, (jumpBarHeightPortrait + 49.0))];
jumpBarContainerPortrait.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
// add jumpbar container to view
[self.view insertSubview:jumpBarContainerPortrait belowSubview:actionTabBar];
[UIView animateWithDuration:0.6
delay:0.0f
options:UIViewAnimationCurveEaseIn
animations:^{
jumpBarContainerPortrait.frame = CGRectMake(0.0, (367 - jumpBarHeightPortrait), 320.0, (jumpBarHeightPortrait + 49.0)); // display jumpBar
} completion:^(BOOL finished) {
if (finished) {
NSLog(#"YAY!");
}
}];
}
else if ([jumpBarContainerPortrait superview]) {
//unload portrait view
[UIView animateWithDuration:0.6
delay:0.0f
options:UIViewAnimationCurveEaseIn
animations:^{
jumpBarContainerPortrait.frame = CGRectMake(0.0, 480.0, 320.0, (jumpBarHeightPortrait + 49.0)); // display jumpBar
// remove selected tabButton highlight
[actionTabBar setSelectedItem:nil];
} completion:^(BOOL finished) {
if (finished) {
// remove subView for superView
[jumpBarContainerPortrait removeFromSuperview];
}
}];
}
}
else if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight)
{
if (![jumpBarContainerLandscape superview]) {
// load landscape view
jumpBarContainerLandscape = [[UIView alloc] initWithFrame:CGRectMake(0.0, 320, 480.0, (jumpBarHeightLandscape + 49.0))];
jumpBarContainerLandscape.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
// add jumpbar container to view
[self.view insertSubview:jumpBarContainerLandscape belowSubview:actionTabBar];
[UIView animateWithDuration:0.6
delay:0.0f
options:UIViewAnimationCurveEaseIn
animations:^{
jumpBarContainerLandscape.frame = CGRectMake(0.0, (207 - jumpBarHeightLandscape), 480.0, (jumpBarHeightLandscape + 49.0)); // display jumpBar
} completion:^(BOOL finished) {
if (finished) {
NSLog(#"YAY!");
}
}];
}
else if ([jumpBarContainerLandscape superview]) {
// remove landscape view
[UIView animateWithDuration:0.6
delay:0.0f
options:UIViewAnimationCurveEaseIn
animations:^{
jumpBarContainerLandscape.frame = CGRectMake(0.0, 320, 480.0, (jumpBarHeightLandscape + 49.0)); // display jumpBar
[actionTabBar setSelectedItem:nil];
} completion:^(BOOL finished) {
if (finished) {
// remove subView for superView
[jumpBarContainerLandscape removeFromSuperview];
}
}];
}
}
}
// if device rotated selected
else if (changeView == 0) {
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait)
{
if([jumpBarContainerLandscape superview])
{
// Device is changing from landscape to protrait change views to fit
// load landscape view
jumpBarContainerPortrait = [[UIView alloc] initWithFrame:CGRectMake(0.0, (367 - jumpBarHeightPortrait), 320.0, (jumpBarHeightPortrait + 49.0))];
jumpBarContainerPortrait.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
jumpBarContainerPortrait.alpha = 1.0;
// add jumpbar container to view
[UIView transitionFromView:jumpBarContainerLandscape
toView:jumpBarContainerPortrait
duration:animationSpeed
options:UIViewAnimationOptionTransitionCrossDissolve
completion:NULL];
[self.view insertSubview:jumpBarContainerPortrait belowSubview:actionTabBar];
}
}
else if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight)
{
if ([jumpBarContainerPortrait superview])
{
// Device is changing from portrait to landscape change views to fit
// load landscape view
jumpBarContainerLandscape = [[UIView alloc] initWithFrame:CGRectMake(0.0, (207 - jumpBarHeightLandscape), 480.0, (jumpBarHeightLandscape + 49.0))];
jumpBarContainerLandscape.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
jumpBarContainerLandscape.alpha = 1.0;
// add jumpbar container to view
[UIView transitionFromView:jumpBarContainerPortrait
toView:jumpBarContainerLandscape
duration:animationSpeed
options:UIViewAnimationOptionTransitionCrossDissolve
completion:NULL];
[self.view insertSubview:jumpBarContainerLandscape belowSubview:actionTabBar];
}
}
}
}
in this example, I have two views landscape and portrait, obviously as the names go each are for their respective orientations.. the logic above goes along the lines of this
if tabbarselected
if !view visible
if device orientation portrait
animate in portrait view.
if device orientation landscape
animate in landscape view
if view visible
if device orientation portrait
animate out portrait view
clear tabbar
if device orientation landscape
animate out landscape view
clear tabbar
if !tabbarselected //meaning listener has identified orientation of device has changed
if device orientation portrait
unload portrait
load landscape
if device orientation landscape
unload landscape
load portrait
I would like to know if there is an easier way than going through all of this hassle! I am still fairly inexperienced so this was my best attempt.. I am hoping someone out there knows of an easier approach than having to do all of this leg work to get views being added to other views as subviews adjusting for orientation properly
any help would be greatly appreciated! I'm desperate lol :)
See the autoresizingMask documentation. Gives you all the same springs and struts control that you have in Interface Builder. E.g.:
CGRect frame = CGRectMake(margin, margin, self.view.frame.size.width - margin * 2, self.view.frame.size.height - margin * 2);
UIView *mySubview = [[UIView alloc] initWithFrame:frame];
[self.view mySubview];
mySubview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Also, if you decide that autoresizingMask is not enough (for example, when you're moving objects with respect to each other to really fine tune the portrait versus landscape orientation), I'd suggest you do this layout process in viewWillLayoutSubviews for iOS5, (or willAnimateRotationToInterfaceOrientation in iOS4 or earlier). This way you don't need to animate the change yourself and the animation will be done in conjunction with the rest of the screen rotation animation.
I am trying to get my UIView to fill the entire screen. I've tried the code below, which works, but always leaves a white line where the status bar was below. No matter what size i set the frame of the UIView, this line always remains there. What am I missing?
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
[view setFrame:[[UIScreen mainScreen] applicationFrame]];
[view setNeedsLayout];
It's highly likely that your view controller's view's frame was set to the applicationFrame and since you're adding the view as it's subview, the frame of your view will be within its parent view's bounds.
You will have to adjust the view controller's view first and then follow it up by adding the view.
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
self.view.frame = [[UIScreen mainScreen] applicationFrame];
view.frame = self.view.bounds;
[view setNeedsLayout];
You will also need to set the autoresizingMask if you think self.view.frame might change.
I have a UINavigationController that pushes on another UIViewController. In this UIViewController I am going to show a UITableView when in portrait mode and another view in landscape mode.
Therefore in my viewDidLoad I am creating UIView and then adding 2 ViewControllers to this. My problem is that when it loads up I get the following white margin at the top.
I think this is because of (in my Step 3 below ) the
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
[[self view] setFrame:appFrame];
is not returning the full screen, minus the navigation bar. Is this right? If so how can I make it return the full size so there is no white margin?
- (void)viewDidLoad
{
[super viewDidLoad];
// Step 1 - Set up the tableDataView for vertical layout
TableDataViewController *tableController = [[TableDataViewController alloc] init];
self.tableDataViewController = tableController;
[tableController release];
// Step 2 - Set up the graphView for horizontal layout
GraphViewController *graphController = [[GraphViewController alloc]
initWithNibName:#"GraphViewController" bundle:nil];
self.graphViewController = graphController;
[graphController release];
// Step 3 - Get the screen size
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
[[self view] setFrame:appFrame];
// Step 4 - Add the vertical view to the containerView
// and then add the containerView to this controller
containerView = [[[UIView alloc] initWithFrame:appFrame] autorelease];
[[self view] addSubview:containerView];
[containerView addSubview:[tableDataViewController view]];
// Step 5 - Add to the active view so we can test for it later
activeView = [tableDataViewController view];
}
Many thanks
Mike
I think you have an issue with your frame offsets. With the navigation bar enabled the rect you get in appFrame has a y offset of 44.f (the navigation bar's height) - check with NSLog and see if that's true.
Because you are setting the frame of a view that will be placed at the origin it should have x and y origins set to zero. You can do this in a safer manner by checking
CGFloat navHeight = navController.navigationBarHidden ? 0 :
navController.navigationBar.frame.size.height;
In fact I think using the bounds property of [UIScreen mainScreen] may be a better overall solution. It will come with the origin and size set correctly and you shouldn't need to check the presence of the navigation bar.
Check what's going on:
CGRect screenBounds = [[UIScreen mainScreen] bounds]
NSLog(#"%#", NSStringFromCGRect(screenBounds));
CGRect screenFrame = [[UIScreen mainScreen] applicationFrame]
NSLog(#"%#", NSStringFromCGRect(screenFrame));
There are a couple things going on. Above all, frame describes a view's location in its superview's coordinate system.
containerView seems to be added as a subview of a custom view, and its coordinate system origins below the navigation bar, not the application frame.
You want to fill the custom view, so the frame of containerView should be something like
CGRectMake(0.0, 0.0, self.view.bounds.width, self.view.bounds.height);
I got the code from this question: How to hide UITabBarController programmatically? which is brilliant, however the view doesn't expand to fit the space left by the tab bar now.
I have set the appropriate UIViewAutoresizingMasks to the view, but I'm assuming that just because its hidden doesn't mean its not still taking up the space?
Anyway, if I do [self.navigationController setNavigationBarHidden:YES animated:YES]; then the navigation bar moves up and off the screen expanding the view with it.
How can I replicate this behavior for the Tab Bar?
Turns out its not quite possible. Best way is to present a modal view (navigation) controller instead of pushing a view controller.
This worked great for me! (combines solutions from other posts mentioned -580 is randomly large number)
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 580, view.frame.size.width,
view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y,
view.frame.size.width, view.frame.size.height +40)];
}
}
-(void)hideTabBar
{ UITabBarController * tabbarcontroller= appDelegate.tabBarVC;
if (tabbarcontroller.tabBar.isHidden)
{
return;
}
tabbarcontroller.tabBar.hidden=YES;
CGRect frm=tabbarcontroller.view.frame;
frm.size.height += tabbarcontroller.tabBar.frame.size.height;
tabbarcontroller.view.frame=frm;
}
-(void)showTabBar
{ UITabBarController * tabbarcontroller=appDelegate.tabBarVC;
if (!tabbarcontroller.tabBar.isHidden)
{
return;
}
CGRect frm=tabbarcontroller.view.frame;
frm.size.height -= tabbarcontroller.tabBar.frame.size.height;
tabbarcontroller.view.frame=frm;
tabbarcontroller.tabBar.hidden=NO;
}
here appDelegate is = (AppDelegate *) [[UIApplication sharedApplication] delegate]
tabBarVc is UITabBarController *tabBarVC defined as property in app delegate
in NSContraints era, do NOT try to modify frame by code, bad things may happen.
Use:
pushedViewController.hidesBottomBarWhenPushed = YES;
typically set hidesBottomBarWhenPushed to yes in prepareforSegue, ANYWAY before iOS actually pushes the new controller.
The easiest way is probably to set a new frame for the view:
CGRect viewFrame = view.frame;
viewFrame.size.height += 40; // Change this to the height of the tab bar
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.75];
view.frame = viewFrame;
[UIView commitAnimations];