I'm writing an app that uses UITabBar for parts of the navigation. I'm also using UIScrollView for presenting more information than what the screen can typically handle. Because of this, I'm needing to set the scroll view to take into account the height of the UITabBar so that all of the information is displayed.
Is there a way to calculate the height of the UITabBar?
If the view controller has an ancestor that is a tab bar controller, you can retrieve the height from that tab bar.
CGFloat tabBarHeight = self.tabBarController.tabBar.frame.size.height;
It is 320 x 49.
If you want to test, open Interface Builder, add a UITabBar, go into the ruler, you will see it
UITabBar is inherited from UIVIew so you can use the frame.size.height to get the height
In Swift:
let height = self.tabBarController?.tabBar.frame.height ?? 49.0
Relying on the actual height of the tab-bar, and using the magic number as a fallback.
Swift 3+
let tabBarHeight = tabBarController?.tabBar.frame.size.height
print(tabBarHeight ?? "not defined")
It should print 49.0 (Type CGFloat)
I was looking to do something similar with centering a label in the VISIBLE portion of a ViewController's view. This ViewController belonged to a UITabBarController.
Here's the code I used to center my label:
UILabel *roomLabel = [[UILabel alloc] init];
CGRect frame = [[self view] bounds];
float tabBarHeight = [[[super tabBarController] tabBar] frame].size.height;
frame.size.height -= tabBarHeight;
[roomLabel setFrame:frame];
[[self view] addSubview:roomLabel];
[roomLabel release];
Notice that I used [[self view] bounds] not [[self view] frame] because the latter includes the 20 pixel top bar as the Y offset (which throws off the vertical centering).
Hope this helps someone!
By the way: I'm using iOS 4.3 and XCode 4 and the "hard-code" value for the TabBar's height is still 49 for me!
I know this isn't ideal, but I really didn't want to have a magic number constant anywhere. What I did was create a throwaway UITabBarController, and get the height from there.
I did this also because [UITabBar initWithFrame:] works as desired, but doing a [bar setFrame:] doesn't. I needed the frame to be correct at creation.
UITabBarController *dtbc = [[[UITabBarController alloc] init] autorelease];
CGRect tabRect = [[[self navigationController] view] frame];
tabRect.origin.y = tabRect.size.height - [[dtbc tabBar] frame].size.height;
tabRect.size.height = [[dtbc tabBar] frame].size.height;
tabBar_ = [[UITabBar alloc] initWithFrame:tabRect];
What I like about this is that it will correctly place the tab bar at the bottom of the parent regardless of the parents size.
This should work in most cases on any instance of UIViewController:
bottomLayoutGuide.length
In swift 4 and 5. self.tabBarController?.getHeight()
extension UITabBarController{
func getHeight()->CGFloat{
return self.tabBar.frame.size.height
}
func getWidth()->CGFloat{
return self.tabBar.frame.size.width
}
}
Others can also try to get the height using the intrinsicContentSize property of the tab bar.
let tabBarHeight = self.tabBarController.tabBar.intrinsicContentSize.height
This is how I got it to work in swift 4.1
let tabBarHeight = CGFloat((self.tabBarController?.tabBar.frame.size.height)!)
Swift 5
if let tabBarController = tabBarController {
let tabBarSafeAreaHeight = tabBarController.tabBar.frame.size.height -
tabBarController.tabBar.safeAreaInsets.bottom
}
This calculates the height of the UITabBar taking into account the safeAreaInsets (UIEdgeInsets)
At the time of writing this equals 49 on iPhone portrait
let screenHeight = UIApplication.shared.statusBarFrame.height +
self.navigationController!.navigationBar.frame.height + (tabBarController?.tabBar.frame.size.height)!
This works perfectly, based it of a few ppls answer here
SWIFT 5 UPDATE :
AS this thread is old, I am posting here the update from another thread: https://stackoverflow.com/a/25550871/14559220. To sum things up,
in portrait and regular landscape, the height is still 49 points. In
compact landscape, the height is now 32 points.
On iPhone X, the height is 83 points in portrait and 53 points in
landscape.
Related
I've got an app that uses a UIWebView. Currently I create the web view using a storyboard but I want to create it programatically.
This needs to work across all devices and iOS 5 - 7.
Currently (using the storyboard) in iOS 6 it looks like this:
But in iOS 7 you don't get that white bar at the top:
How can I create the UIWebView programatically so that it fills available window space (i.e. doesn't go over tab bar or status bar) across all devices?
Thanks
//EDIT
Here's my storyboard for the UIWebView
//Edit 2
As per a suggestion I am doing this but it overlaps the status bar and tab bar. Is there any way to take them into account?
float width = [UIScreen mainScreen].bounds.size.width;
float height = [UIScreen mainScreen].bounds.size.height;
try to set your webview's frame programmatically like this
float width = [UIScreen mainScreen].bounds.size.width;
NSLog(#"width: %f", width);
float height = [UIScreen mainScreen].bounds.size.height;
NSLog(#"height: %f", height);
so your webview's frame should be
objWeb.frame = CGRectMake(0,0,width,height);
Okay, self.view will be resized at somewhere between viewDidLoad and viewWillAppear.
So, if you wanted to adjust the size, you should do it in viewWillApear.
But, my suggestion is use AutoResizeMask, or |[webView]| for auto layout.
AutoReszie
add your webView to self.view as subView, set the frame to self.view.boudse, and then
webView.autoResizeMask = UIViewAutoResizeMaskFliexbleHeight|UIViewAutoResizeMaskFliexbleWidth;
let me know if you need autoLayout version.
This is what worked for me based on others answers
-(void)createWebView
{
float width = self.view.bounds.size.width;
float height = self.view.bounds.size.height;
UIWebView *wv = [[UIWebView alloc] initWithFrame:CGRectMake(0, 20, width, height)];
wv.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
wv.scalesPageToFit = YES;
wv.delegate = self;
[self.view addSubview:wv];
}
I took a regular UITabBar and changed it's background image to a custom one which has a lower height, so I changed the height of the frame.
At first what I got is a blank space below the tab bar. so I changed the origin of the frame too. But now the blank space has moved up above the tab bar and this is the result:
And this is the code declaring the tab bar in the AppDelegate:
self.tabContoller = [[UITabBarController alloc] init];
//customizing the tabbar
UIImage * tabBackgroundImage = [UIImage imageNamed:#"tabBarBg.png"];
self.tabContoller.tabBar.backgroundColor = [UIColor colorWithRed:245.f/255.f green:245.f/255.f blue:245.f/255.f alpha:255.f/255.f];
self.tabContoller.tabBar.backgroundImage = tabBackgroundImage;
//setting the tabbar height to the correct height of the image
CGRect tabR = self.tabContoller.tabBar.frame;
CGFloat diff = tabR.size.height - tabBackgroundImage.size.height;
tabR.size.height = tabBackgroundImage.size.height;
tabR.origin.y += diff;
self.tabContoller.tabBar.frame = tabR;
I guess that the problem is that the ViewControllers draw themselves above a constant space which is the height of the regular tab bar. Is there any way to change it?
Change your UITabBarController's subviews to a full-sized frame, this worked for me:
[[yourTabBarController.view.subviews objectAtIndex:0] setFrame:CGRectMake(0, 0, 320, 480)];
Try creating your own class extending from UITabBar and use this function:
- (CGSize)sizeThatFits:(CGSize)size {
CGSize auxSize = size;
auxSize.height = 54; // Put here the new height you want and that's it
return auxSize;
}
This will resize the UITabBar to the size you want. Simple and easy.
If changing the frame like #JoaT mentioned doesn't work make sure the view controller's view has the correct autoresizing mask set.
This SO link may be helpful.
I tried by changing the height and origin of tabbar, for me it worked properly.You can try by increasing the height of your viewcontroller.
The width of a UITabBarItem varies, depending on how many there are.
How do I determine how wide my tab bar items are?
I'm looking for a property if possible, as opposed to a mathematical formula, since on the iPad, there is also an issue of padding on either side of the tab bar. Consider these screenshots. Notice the padding on either side of the tab bar items on the iPad (highlighted with the red boxes). This padding does not exist on the iPhone.
The iPad:
The iPhone:
Edit: It has been noted in the comments below that this solution did not work in a beta version of iOS 5, so be prepared to modify it to meet your needs.
I think in order to do this the right way, you have to be able to get to the frame of each tab bar item. Fortunately, this is possible:
CGFloat tabBarTop = [[[self tabBarController] tabBar] frame].origin.y;
NSInteger index = [[self tabBarController] selectedIndex];
CGFloat tabMiddle = CGRectGetMidX([[[[[self tabBarController] tabBar] subviews] objectAtIndex:index] frame]);
The above code gives you the y coordinate of the top of the tab bar and the x coordinate of the middle of the selected tab item. From here, you can add your indicator view to the tab bar controller's view relative to this point.
Disclaimer: The danger with this approach is that it peeks under the hood of the UITabBar class by accessing its view hierarchy and the frame property of instances of the private UITabBarButton class. It is possible (however unlikely) that this approach could be affected/broken by a future iOS update. However, you're not accessing any private APIs, so this shouldn't get your app rejected from the App Store.
I made a simple iPad project demonstrating how it works, complete with animations.
Swift 3
I created a shared instance in UIApplication then pulled my subviews width
tabBarController?.tabBar.subviews[1].frame.width
UITabBarItem inherits from UIBarItem, which inherits from NSObject. Since it doesn't inherit from UIView, I don't know that you're going to be able to get the information you want just with a property. I know you don't want a mathematical way of doing it, but it would seem that getting the frame of the tab bar and dividing by the # of tabs would be a reasonable option that should work regardless of the hardware (iphone vs ipad). Padding shouldn't affect this, right? So you'd have:
tabSize = tabbar.frame.size.width / [tabbar.items count];
tabBarStart = tabbar.frame.origin.x;
// Assume index of tabs starts at 0, then the tab in your pic would be tab 4
// targetX would be the center point of the target tab.
targetX = tabBarStart + (tabSize * targetTabIndex) + (tabSize / 2);
I, as well, am interested to know if someone finds a simple property to give this information.
Looking at the answer above:
Items in the UITabBar work different in the iPhone and the iPad. In the iPhone they fill whole width, in the iPad they're centered horizontally.
There is a spacing between the items in the iPad.
You can set the values for both width and spacing using tabBar.itemWidth or tabBar.itemSpacing. You are not able to read system spacing or width from it - you'll receive 0 unless you set it.
So here's my sample how to get frames of all UITabBarItems:
// get all UITabBarButton views
NSMutableArray *tabViews = [NSMutableArray new];
for (UIView *view in self.tabBar.subviews) {
if ([view isKindOfClass:[UIControl class]]) {
[tabViews addObject:[NSValue valueWithCGRect:view.frame]];
}
}
// sort them from left to right
NSArray *sortedArray = [tabViews sortedArrayUsingComparator:^NSComparisonResult(NSValue *firstValue, NSValue *secondValue) {
CGRect firstRect = [firstValue CGRectValue];
CGRect secondRect = [secondValue CGRectValue];
return CGRectGetMinX(firstRect) > CGRectGetMinX(secondRect);
}];
Now you have a table of frames, corresponding to your tabbar items. You can eg. place an imageView on selected index button frame.
CGRect frame = self.tabBar.bounds;
CGSize imageSize = CGSizeMake(CGRectGetWidth(frame) / self.tabBar.items.count, self.imageView.image.size.height);
CGRect selectedRect = sortedArray.count > self.selectedIndex ? [sortedArray[self.selectedIndex] CGRectValue] : CGRectZero;
[self.imageView setFrame:CGRectIntegral(CGRectMake(CGRectGetMinX(selectedRect), CGRectGetMaxY(frame) - imageSize.height,
CGRectGetWidth(selectedRect), imageSize.height))];
I tried these 2 properties of UITabBar:
#property(nonatomic) CGFloat itemWidth NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;
#property(nonatomic) CGFloat itemSpacing NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;
But get zero value as Vive said. So I wrote some codes to get the right width:
CGFloat tabBarItemWidth = 0;
for (UIView *view in [self.tabBarController.tabBar subviews]) {
if ([view isKindOfClass:NSClassFromString(#"UITabBarButton")]) {
if (tabBarItemWidth == 0) {
tabBarItemWidth = view.frame.origin.x;
} else {
tabBarItemWidth = view.frame.origin.x - tabBarItemWidth;
break;
}
}
}
Why not use the width of first UITabBarButton? Because there are spaces between tab bar buttons. :)
You can get tabBarItem view using private property view. Then just get view frame.
- (CGRect)rectForTabBarItem:(UITabBarItem *)tabBarItem {
UIView *itemView = [tabBarItem valueForKey:#"view"];
return itemView.frame;
}
Loop through your tabBar's subviews and find the views with the class "UITabBarButton". These are the views for each tab item.
for (UIView *view in self.tabBar.subviews) {
if ([view isKindOfClass:NSClassFromString(#"UITabBarButton")]) {
// view.frame contains the frame for each item's view
// view.center contains the center of each item's view
}
}
I need to resize some elements in relation to the height of the iPhone's Status Bar. I know that the status bar is usually 20 points high but this isn't the case when it's in tethering mode. It gets doubled to 40. What is the proper way to determine it's height? I've tried
[[UIApplication sharedApplication] statusBarFrame]
but it gives me 20 x 480 in landscape which is correct but then it gives me 320 x 40 in portrait. Why isn't it giving me the opposite of that (40 x 320)?
The statusBarFrame returns the frame in screen coordinates. I believe the correct way to get what this corresponds to in view coordinates is to do the following:
- (CGRect)statusBarFrameViewRect:(UIView*)view
{
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGRect statusBarWindowRect = [view.window convertRect:statusBarFrame fromWindow: nil];
CGRect statusBarViewRect = [view convertRect:statusBarWindowRect fromView: nil];
return statusBarViewRect;
}
Now in most cases the window uses the same coordinates as the screen, so [UIWindow convertRect:fromWindow:] doesn't change anything, but in the case that they might be different this method should do the right thing.
Did you do it like this:
CGRect rect;
rect = [[UIApplication sharedApplication] statusBarFrame];
NSLog(#"Statusbar frame: %1.0f, %1.0f, %1.0f, %1.0f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
EDIT
The iOS 11 way to work out where to put the top of your view content is UIView's safeAreaLayoutGuide See UIView Documentation.
DEPRECATED ANSWER
If you're targeting iOS 7+, The documentation for UIViewController advises that the viewController's topLayoutGuide property gives you the bottom of the status bar, or the bottom of the navigation bar, if it's also visible. That may be of use, and is certainly less hack than many of the previous solutions.
You could test which is the lesser of the two values, that will be the real height.
This method works for portrait & landscape orientation.
-(float) statusBarHeight
{
CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
return MIN(statusBarSize.width, statusBarSize.height);
}
// example call
float statusBarHeight = [self statusBarHeight];
Swift 2:
let statusBarHeight = UIApplication.sharedApplication().statusBarFrame.height
Swift 3 or Swift 4:
let statusBarHeight = UIApplication.shared.statusBarFrame.height
Advice: Don't forget to inject the UIApplication.shared do not just use the singleton in your code.
Here is the Swift version if anyone needs it:
var screenStatusBarHeight: CGFloat {
return UIApplication.sharedApplication().statusBarFrame.height
}
This is included as a standard variable in:
https://github.com/goktugyil/EZSwiftExtensions
Disclaimer: Its my repo
I'm developing an app in objective-c and in that app set the navigationBar to translucent(through IB). But the problem is that the view displayed behind the navigation bar.
Anybody else tried working with translucent navigation bars?
regards
Jayaraj
The [navigationController view] automatically resizes to "underlap" translucent navigation bars as of OS 3.0
You can simply add 44 pixels to the y value of the origin property to overcome this.
You can use 44 if you know you're in portrait (not landscape, in which the height of the navBar is less than 44)
You can also do:
// applicationFrame subtracts the height of the statusBar if it's visible.
// bounds doesn't take into account the statusBar.
CGRect navFrame = [[UIScreen mainScreen] applicationFrame];
NSLog(#"navFrame: %f x %f", navFrame.size.width, navFrame.size.height);
navFrame.size.height -= self.navigationController.navigationBar.frame.size.height;
NSLog(#"navFrame: %f x %f", navFrame.size.width, navFrame.size.height);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:navFrame];
NSLog(#"imageView: %#", imageView);
I learned this from reading three20's source code. You can find it on github.com
Matt