I'm working on a app, in which I need to keep a navigation bar.
when I write any title on the bar, the time and the title kinda get very close to each other. I wanted to increase the height of the bar, so it can get some breathing room.
select your ViewController --> select your Navigation Item --> Prompt --> Add space it increase the height of **Navigation bar**
Check Image here :
Programatically
Add this in viewWillAppear or viewDidAppear method
Objective-C
[self.navigationController.navigationBar setFrame:CGRectMake(0, 0, self.view.frame.size.width,80.0)];
Swift
self.navigationController.navigationBar.frame = CGRectMake(0, 0, self.view.frame.size.width, 80.0)
Swift-3
self.navigationController!.navigationBar.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 80.0)
iOS 11
objective C
for (UIView *subview in self.navigationController.navigationBar.subviews) {
if ([NSStringFromClass([subview class]) containsString:#"BarBackground"]) {
CGRect subViewFrame = subview.frame;
// subViewFrame.origin.y = -20;
subViewFrame.size.height = 100;
[subview setFrame: subViewFrame];
}
}
swift
for subview in (self.navigationController?.navigationBar.subviews)! {
if NSStringFromClass(subview.classForCoder).contains("BarBackground") {
var subViewFrame: CGRect = subview.frame
// subViewFrame.origin.y = -20;
subViewFrame.size.height = 100
subview.frame = subViewFrame
}
}
simply add this line to your viewController
navigationController?.additionalSafeAreaInsets.top = 30
// where 30 is the extra space, add as per your need.
Apple proposes not to resize navigationBar itself, but remove shadow from bar and add custom view under your navigationBar. This can work for most cases. Check Apple's samples.
Add the following extension to your project:
import UIKit
extension UINavigationBar {
override open func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: UIScreen.main.bounds.size.width, height: 80.0)
}
}
Please refer the apple recommended approach for extended navigation bar here,
https://developer.apple.com/library/content/samplecode/NavBar/Introduction/Intro.html
THIS SOLUTION NO LONGER WORKS IN Xcode 8.x.x and later!
you can also increase height without creating the custom navigation follow the following steps
Step 1 Selecte Navigation bar in Storyboard or XIB
Step 2 Copy ObjectID from Identity Inspector
Step 3 Open Storyboard/XIB as Source Code
Step 4 Find ObjectID in Source Code past ObjectID in search
Step 5 Edit height! thats all
I hope this will help you
Add this in viewWillAppear method
CGFloat height = 80;
[self.navigationController.navigationBar setFrame:CGRectMake(0, 0,
self.view.frame.size.width,height)];
if it increase first and shrinks to original height then add this code in viewDidAppear method
We need to change the height of the navigation bar for each time the view show.So put the code on viewWillAppear
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 80)
}
we can set the width as the width of the view and change the height as we wish.
You can't change the height of the default NavigationBar if I'm not wrong.
Although, you can create a custom NavigationBar and add a custom height to it.
navigationController?.additionalSafeAreaInsets.top = 25
Add this to viewDidLoad. it will definitely work. Successfully worked in Xcode 12-version
[self.navigationController.navigationBar setPrefersLargeTitles:YES];
is going to Increase the Navigation bar height Programmatically
Related
I have added navigation bar into application window but navigation bar collides with status bar. (I need to do it with application window).
Code I’ve tried is:
UIWindow * firstWindow = [UIApplication sharedApplication].windows[0];
UINavigationBar * navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, firstWindow.frame.size.width, 64)];
/*
// I tried these also
UINavigationBar * navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, firstWindow.frame.size.width, 84)];
UINavigationBar * navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 20, firstWindow.frame.size.width, 64)];
*/
[firstWindow addSubview:navBar];
Here are results:
I looked around these and tried all answers related to default navigation bar but nothing works:
What is the height of Navigation Bar in iOS 7?
iOS 10 custom navigation bar height
iOS 7 status and navigation bar different height in storyboard than in app
UINavigationBar/Status Bar issue in IOS7
When hiding the statusbar my navigation bar moves up in iOS7
iOS 7 Status Bar Collides With NavigationBar using ViewController
Use additionalSafeAreaInsets for NavigationController,
self.navigationController.additionalSafeAreaInsets = UIEdgeInsetsMake(20, 0, 0, 0);
This is how you would do it in iOS 11 using the safe area insets. You need a view controller under the hood so you can at least get notified when the safe area insets are updated. This code is from the single app template in Xcode 9. You can see the frame update between portrait and landscape mode as the safe areas are updated.
import UIKit
class ViewController: UIViewController {
private var window: UIWindow!
private var navbar: UINavigationBar!
override func viewDidLoad() {
super.viewDidLoad()
self.window = UIApplication.shared.windows[0]
self.navbar = UINavigationBar(frame: CGRect(x: 0, y: self.view.safeAreaInsets.top, width: window.frame.size.width, height: 64))
self.navbar.barTintColor = .red
window.backgroundColor = .gray
window.addSubview(navbar)
self.view.backgroundColor = .clear
}
override func viewSafeAreaInsetsDidChange() {
let navBarHeight: CGFloat = 64
self.navbar.frame = CGRect(x: 0, y: self.view.safeAreaInsets.top, width: self.window.bounds.width, height: navBarHeight)
}
}
Here is an image from the simulator:
If you need to support iOS 10 and below then you need to also use the topLayoutGuide which was deprecated. The length property will give you similar information as the safe layout guides.
I feel compelled to add though that finding a way to use the navigation controller would be much easier and safer to use especially as you start supporting multiple versions of iOS and Apple upgrades UIKit.
Try this
CGFloat height = [UIApplication sharedApplication].statusBarFrame.size.height;
UIWindow * firstWindow = [UIApplication sharedApplication].windows[0];
UINavigationBar * navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, height, firstWindow.frame.size.width, 64)];
[firstWindow addSubview:navBar];
Whatever Im trying to my view controller won't appear offscreen (as it I want to, because I have a handler(menu) that will make the animation from offscreen to onscreen. After realising (from other posts) that I can't change a views frame before it appears, makes sense to use this approach:
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//self.view.frame = CGRectInset(self.view.frame, 100, 50);
self.view.frame = CGRectMake(50, 50, 50, 50);
}
But still, it is presented exactly on screen as any standard view controller would (0,0,width, height)
How to solve?
You have the method willLayoutSubviews: or viewDidLayoutSubviews
Called to notify the view controller that its view is about to layout
its subviews.When a view's bounds change, the view adjusts the position of its subviews.
So instead of doing it in view will appear,over ride the willLayoutSubviews:
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
[self.view setFrame:CGRectMake(100, 100, 100, 100)];
}
Have you considered adding all of your views to another view, functioning as a container view, and then adding that one view to your view controller's view? Then you'd be able to position that container view freely before it appears.
You could access the ViewControllers view property and set it in the loadView() and it will only be set once and therefore not messing up any custom animations.
The view controller calls this method when its view property is requested but is currently nil. This method loads or creates a view and assigns it to the view property.
Because viewWillLayoutSubviews() would be setting the frame every time subviews are laid out.
override func loadView() {
super.loadView()
self.view.frame = CGRect(x: self.view.bounds.origin.x,
y: self.view.bounds.origin.y,
width: self.view.bounds.width,
height: self.view.bounds.height * 0.8
}
I am trying to have a transparent navigation bar in IOS 7 app. There is a full screen image in my application. I am also having a UITableView over that image. When I use the code below, image fits the screen as I want but UITableView goes under navigation bar.
in viewDidLoad
i use
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.navigationController.navigationBar.translucent = YES;
self.navigationController.view.backgroundColor = [UIColor clearColor];
it is being ok when I change to self.navigationController.navigationBar.translucent = NO; but then I lose transparency at navigation bar.
You could set the contentInsets of your tableView so it is initially below the navigation bar, but would scroll behind it (content would be overlapping)
self.tableView.contentInset = UIEdgeInsetsMake(44,0,0,0);
Or you could offset the frame of the tableview. Then the scrolling content would be cut off below the navigation bar (which wouldn't look good, too)
I my case helped this one (modified version of Bill Chan's code):
Objective C version:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGRect rect = self.navigationController.navigationBar.frame;
float y = rect.size.height + rect.origin.y;
self.tableView.contentInset = UIEdgeInsetsMake(y, 0, 0, 0);
}
The point is that table have to be pushed down for the height of navigationBar (rect.size.height) plus status bar height (rect.origin.y);
Swift version (also compatible with Swift 2):
override func viewDidLayoutSubviews() {
if let rect = self.navigationController?.navigationBar.frame {
let y = rect.size.height + rect.origin.y
self.tableView.contentInset = UIEdgeInsetsMake( y, 0, 0, 0)
}
}
I had the similar problem for iOS 9. When I first open viewController, tableView is under top bar. Then after scrolling tableView everything works fine.
Select your view controller
Click the 'Attributes Inspector' tab
Uncheck 'Under Top Bars'
Set the y-position of tableview to height of the navigation bar plus height of the status bar (let it be height)
i.e,
height = 64; // height of navigation bar = 44(In portait), height of status bar = 20
tableView.frame = CGRectMake(tableView.frame.origin.x, height , tableView.frame.size.width, tableView.frame.size.height);
If you are using autolayout just change the update the tableView top constraint instead of changing frame.
and also change viewController automaticallyAdjustsScrollViewInsets to NO
self.automaticallyAdjustsScrollViewInsets = NO;
If you are supporting different orientation update frame and contentInset to (52) because navigation bar height in landscape mode is 32.
check this Sample
This is working in both landscape mode and portrait mode in iOS8:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGRect rect = self.navigationController.navigationBar.frame;
float y = -rect.origin.y;
self.tableView.contentInset = UIEdgeInsetsMake(y ,0,0,0);
}
Better not to hardcode the Inset values as it might based on the orientation of the device.
Code:
func setTableViewContentInset() {
let contentInsetHeight = topLayoutGuide.length
let contentInset = UIEdgeInsetsMake(contentInsetHeight, 0, 0, 0)
tableView.contentInset = contentInset
tableView.scrollIndicatorInsets = contentInset
}
func scrollToTop() {
if tableView.indexPathsForVisibleRows?.count > 0 {
let topIndexPath = NSIndexPath(forRow: 0, inSection: 0)
tableView.scrollToRowAtIndexPath(topIndexPath, atScrollPosition: .Top, animated: false)
}
}
func scrollToTopOfVisibleCells() {
if let visibleIndexPaths = tableView.indexPathsForVisibleRows where tableView.indexPathsForVisibleRows?.count > 0 {
let topMostVisibleIndexPath = visibleIndexPaths[0]
tableView.scrollToRowAtIndexPath(topMostVisibleIndexPath, atScrollPosition: .Top, animated: false)
}
}
//MARK: Load Views
override func viewDidLoad() {
super.viewDidLoad()
setTableViewContentInset()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
scrollToTop()
}
//MARK: Trait collection change
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
setTableViewContentInset()
scrollToTopOfVisibleCells()
}
Solutions that introduce a magic constant don't scale most of the time. For example, if the next iPhone introduces a different navigation bar height we'll have to update our code.
Fortunately, Apple provided us cleaner ways of overcoming this issue, for example topLayoutGuide:
The topLayoutGuide property comes into play when a view controller is
frontmost onscreen. It indicates the highest vertical extent for
content that you don't want to appear behind a translucent or
transparent UIKit bar (such as a status or navigation bar)
Programmatically you can achieve with the following code snippet (the same can be achieved via IB too):
override func viewDidLoad() {
super.viewDidLoad()
automaticallyAdjustsScrollViewInsets = false
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.topAnchor.constraint(equalTo:
topLayoutGuide.bottomAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
Note: topLayoutGuide is deprecated in iOS 11, we should use the safeAreaLayoutGuide property of UIView instead.
Introduction
I am new to both iOS development and Stack Overflow, so forgive me if my post isn't perfect.I also had this issue, and when I used the content insets for my UITableView it worked perfectly upon loading first, or when visiting it from my other tabs; however, if I navigated back to the view, it would have the extra "padding". I figured out a work around, so that my UITableView will be correctly placed every time.
The Issue
When you first load the UITableView, or tab to it, it needs the insets to correctly start the table below the navigation bar, but when you navigate back it does not need the insets, because for some reason, it correctly calculates for the placement of the UITableView. This is why you can get the extra padding.
The Solution
The solution involves using a boolean to determine whether you have navigated away, so that it can correctly determine whether it needs the content insets or not.In -(void)viewDidLoad I set hasNavigatedFurther = NO. Then:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (!hasNavigatedFurther) {
self.tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
} else {
self.tableView.contentInset = UIEdgeInsetsZero;
//In order to allow visiting between tabs and retaining desired look
hasNavigatedFurther = NO;
}
}
In order to make this work, you need to set hasNavigatedFurther = YES just before your code that pushes another view onto the navigation stack.
-(void)btnTouched:(id)sender {
hasNavigatedFurther = YES;
NextViewController* nvc = [NextViewController new];
[self.navigationController pushViewController:nvc animated:YES];
}
I came up with the following solution, which, on navigating to the view controller for the first time, adjusts the table view's contentInset for the navigation bar's height, taking into account any padding that the top cell might have. When returning to this view controller after pushing another view controller onto the stack, I then re-adjust the contentInset to UIEdgeInsetsZero:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self adjustEdgeInsetsForTableView];
}
- (void)adjustEdgeInsetsForTableView {
if(self.isMovingToParentViewController) {
self.tableViewForm.contentInset = UIEdgeInsetsMake(self.navigationController.navigationBar.frame.size.height + padding, 0, 0, 0);
} else {
self.tableViewForm.contentInset = UIEdgeInsetsZero;
}
}
I combined #Adam Farrell and #Tash Pemhiwa 's solutions, and finally the code below works for me:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self adjustEdgeInsetsForTableView];
}
- (void)adjustEdgeInsetsForTableView
{
if(self.isMovingToParentViewController) {
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
} else {
self.tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
}
}
Hope this will help people who waste couple of hours on this weird UI behavior.
Constrain the table view to the bottom of the navigation bar. The table view will automatically be offset by 44, but then in code we can just do this:
tableView.contentInset = UIEdgeInsets(top: -44, left: 0, bottom: 0, right: 0)
The bar is transparent and has no color, but the table view does not overlap it at all. Notice the word "Hook" gets cut off despite the navigation bar being transparent. This will only work of you constrain the table view top edge to be 0 from the navigation bar. NOT 0 from the top view.
All you need is love this:
assert(tableView.contentInsetAdjustmentBehavior == .automatic)
there is zero need to do ugly magic constants beardance from iOS 11 onwards
I did not even need to set contentInsetAdjustmentBehavior to .none
to fix navbar underlapping.
.automatic
worked automagically
try to use layoutguide to fix
var constraints = [NSLayoutConstraint]()
let guide = view.safeAreaLayoutGuide
constraints.append(self.tableView.leadingAnchor.constraint(equalTo: guide.leadingAnchor))
constraints.append(self.tableView.trailingAnchor.constraint(equalTo: guide.trailingAnchor))
constraints.append(self.tableView.topAnchor.constraint(equalTo: guide.topAnchor))
constraints.append(self.tableView.bottomAnchor.constraint(equalTo: guide.bottomAnchor))
I am trying to add an image between the status bar and navigation bar in IOS. Is this possible? Can you please guide me how this can be achieved?
Basically I am trying to achieve something like this:
Exactly I don't know, is it possible or not ? But I have an idea such like,
You need to create custom navigationBar for do this you need to hide your navigationBar of your rootView controller when it created.
EX -
self.rootNavigationController.navigationBarHidden = YES;
Then at first top add your custom image with UIImageView with it's specific size, and then after add your custom UINavigationBar (It is UIImageView/UIView OR whatever) with 2 buttons.
you can use a UIImageView an set its image as per below
- (void) viewDidLoad {
topImage = [[UIIImageView alloc] initWithFrame:CGRctZero];
topImage.image = [UIImage imageNamed:#"topim"];
[self.view addSubview:topImage];
// Add your UINavigationController here (or in Interface builder)
}
- (void) viewWillLayoutSubviews {
topImage.frame = CGRectMake(0, 0, 320, 20);
navController.view.frame = CGRectMake(0, 20, 320, self.view.bounds.size.height - 20);
}
I know that the presence of the more view controller (navigation bar) pushes down the UIView by its height. I also know that this height = 44px. I have also discovered that this push down maintains the [self.view].frame.origin.y = 0.
So how do I determine the height of this navigation bar, other than just setting it to a constant?
Or, shorter version, how do I determine that my UIView is showing with the navigation bar on top?
The light bulb started to come on. Unfortunately, I have not discovered a uniform way to correct the problem, as described below.
I believe that my whole problem centers on my autoresizingMasks. And the reason I have concluded that is the same symptoms exist, with or without a UIWebView. And that symptom is that everything is peachy for Portrait. For Landscape, the bottom-most UIButton pops down behind the TabBar.
For example, on one UIView, I have, from top to bottom:
UIView – both springs set (default case) and no struts
UIScrollView - If I set the two springs, and clear everything else (like the UIView), then the UIButton intrudes on the object immediately above it. If I clear everything, then UIButton is OK, but the stuff at the very top hides behind the StatusBar Setting only the top strut, the UIButton pops down behind the Tab Bar.
UILabel and UIImage next vertically – top strut set, flexible everywhere else
Just to complete the picture for the few that have a UIWebView:
UIWebView - Struts: top, left, right Springs: both
UIButton – nothing set, i.e., flexible everywhere
Although my light bulb is dim, there appears to be hope.
Please bear with me because I needed more room than that provided for a short reply comment.
Thanks for trying to understand what I am really fishing for ... so here goes.
1) Each UIViewController (a TabBar app) has a UIImage, some text and whatever on top. Another common denominator is a UIButton on the bottom. On some of the UIViewControllers I have a UIWebView above the UIButton.
So, UIImage, text etc. UIWebView (on SOME) UIButton
Surrounding all the above is a UIScrollView.
2) For those that have a UIWebView, its autoresizingMask looks like:
—
|
—
^
|
|
|—| ←----→ |—|
|
|
V
The UIButton's mask has nothing set, i.e., flexible everywhere
Within my -viewDidLoad, I call my -repositionSubViews within which I do the following:
If there is no UIWebView, I do nothing except center the UIButton that I placed with IB.
If I do have a UIWebView, then I determine its *content*Height and set its frame to enclose the entire content.
UIScrollView *scrollViewInsideWebView = [[webView_ subviews] lastObject];
webViewContentHeight = scrollViewInsideWebView.contentSize.height;
[webView_ setFrame:CGRectMake(webViewOriginX, webViewOriginY,
sameWholeViewScrollerWidth, webViewContentHeight)]
Once I do that, then I programmatically push the UIButton down so that it ends up placed below the UIWebView.
Everything works, until I rotate it from Portrait to Landscape.
I call my -repositionSubViews within my -didRotateFromInterfaceOrientation.
Why does the content height of my UIWebView not change with rotation?.
From Portrait to Landscape, the content width should expand and the content height should shrink. It does visually as it should, but not according to my NSLog.
Anyway, with or without a UIWebView, the button I've talked about moves below the TabBar when in Landscape mode but it will not scroll up to be seen. I see it behind the TabBar when I scroll "vigorously", but then it "falls back" behind the TabBar.
Bottom line, this last is the reason I've asked about the height of the TabBar and the NavigationBar because the TabBar plants itself at the bottom of the UIView and the NavigationBar pushes the UIView down.
Now, I'm going to add a comment or two here because they wouldn't have made sense earlier.
With no UIWebView, I leave everything as is as seen by IB.
With a UIWebView, I increase the UIWebView's frame.height to its contentHeight and also adjust upward the height of the surrounding UIScrollView that surrounds all the sub-views.
Well there you have it.
Do something like this ?
NSLog(#"Navframe Height=%f",
self.navigationController.navigationBar.frame.size.height);
The swift version is located here
UPDATE
iOS 13
As the statusBarFrame was deprecated in iOS13 you can use this:
extension UIViewController {
/**
* Height of status bar + navigation bar (if navigation bar exist)
*/
var topbarHeight: CGFloat {
return (view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0) +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
}
}
With iPhone-X, height of top bar (navigation bar + status bar) is changed (increased).
Try this if you want exact height of top bar (both navigation bar + status bar):
UPDATE
iOS 13
As the statusBarFrame was deprecated in iOS13 you can use this:
extension UIViewController {
/**
* Height of status bar + navigation bar (if navigation bar exist)
*/
var topbarHeight: CGFloat {
return (view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0) +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
}
}
Objective-C
CGFloat topbarHeight = ([UIApplication sharedApplication].statusBarFrame.size.height +
(self.navigationController.navigationBar.frame.size.height ?: 0.0));
Swift 4
let topBarHeight = UIApplication.shared.statusBarFrame.size.height +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
For ease, try this UIViewController extension
extension UIViewController {
/**
* Height of status bar + navigation bar (if navigation bar exist)
*/
var topbarHeight: CGFloat {
return UIApplication.shared.statusBarFrame.size.height +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
}
}
Swift 3
let topBarHeight = UIApplication.sharedApplication().statusBarFrame.size.height +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
Swift version:
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.height
iOS 14
For me, view.window is null on iOS 14.
extension UIViewController {
var topBarHeight: CGFloat {
var top = self.navigationController?.navigationBar.frame.height ?? 0.0
if #available(iOS 13.0, *) {
top += UIApplication.shared.windows.first?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
top += UIApplication.shared.statusBarFrame.height
}
return top
}
}
Swift 5
If you want to get the navigation bar height, use the maxY property that considers the safeArea size as well, like this:
let height = navigationController?.navigationBar.frame.maxY
Support iOS 13 and Below:
extension UIViewController {
var topbarHeight: CGFloat {
if #available(iOS 13.0, *) {
return (view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0) +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
} else {
let topBarHeight = UIApplication.shared.statusBarFrame.size.height +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
return topBarHeight
}
}
}
Did you try this?
let barHeight = self.navigationController?.navigationBar.frame.height ?? 0
UIImage*image = [UIImage imageNamed:#"logo"];
float targetHeight = self.navigationController.navigationBar.frame.size.height;
float logoRatio = image.size.width / image.size.height;
float targetWidth = targetHeight * logoRatio;
UIImageView*logoView = [[UIImageView alloc] initWithImage:image];
// X or Y position can not be manipulated because autolayout handles positions.
//[logoView setFrame:CGRectMake((self.navigationController.navigationBar.frame.size.width - targetWidth) / 2 , (self.navigationController.navigationBar.frame.size.height - targetHeight) / 2 , targetWidth, targetHeight)];
[logoView setFrame:CGRectMake(0, 0, targetWidth, targetHeight)];
self.navigationItem.titleView = logoView;
// How much you pull out the strings and struts, with autolayout, your image will fill the width on navigation bar. So setting only height and content mode is enough/
[logoView setContentMode:UIViewContentModeScaleAspectFit];
/* Autolayout constraints also can not be manipulated since navigation bar has immutable constraints
self.navigationItem.titleView.translatesAutoresizingMaskIntoConstraints = false;
NSDictionary*metricsArray = #{#"width":[NSNumber numberWithFloat:targetWidth],#"height":[NSNumber numberWithFloat:targetHeight],#"margin":[NSNumber numberWithFloat:20]};
NSDictionary*viewsArray = #{#"titleView":self.navigationItem.titleView};
[self.navigationItem.titleView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-(>margin=)-H:[titleView(width)]-(>margin=)-|" options:NSLayoutFormatAlignAllCenterX metrics:metricsArray views:viewsArray]];
[self.navigationItem.titleView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[titleView(height)]" options:0 metrics:metricsArray views:viewsArray]];
NSLog(#"%f", self.navigationItem.titleView.width );
*/
So all we actually need is
UIImage*image = [UIImage imageNamed:#"logo"];
UIImageView*logoView = [[UIImageView alloc] initWithImage:image];
float targetHeight = self.navigationController.navigationBar.frame.size.height;
[logoView setFrame:CGRectMake(0, 0, 0, targetHeight)];
[logoView setContentMode:UIViewContentModeScaleAspectFit];
self.navigationItem.titleView = logoView;
Handy Swift 4 extension, in case it's helpful to someone else. Works even if the current view controller does not display a navigation bar.
import UIKit
extension UINavigationController {
static public func navBarHeight() -> CGFloat {
let nVc = UINavigationController(rootViewController: UIViewController(nibName: nil, bundle: nil))
let navBarHeight = nVc.navigationBar.frame.size.height
return navBarHeight
}
}
Usage:
UINavigationController.navBarHeight()
The light bulb started to come on. Unfortunately, I have not discovered a uniform way to correct the problem, as described below.
I believe that my whole problem centers on my autoresizingMasks. And the reason I have concluded that is the same symptoms exist, with or without a UIWebView. And that symptom is that everything is peachy for Portrait. For Landscape, the bottom-most UIButton pops down behind the TabBar.
For example, on one UIView, I have, from top to bottom:
UIView – both springs set (default case) and no struts
UIScrollView -
If I set the two springs, and clear everything else (like the UIView), then the UIButton intrudes on the object immediately above it.
If I clear everything, then UIButton is OK, but the stuff at the very top hides behind the StatusBar
Setting only the top strut, the UIButton pops down behind the Tab Bar.
UILabel and UIImage next vertically – top strut set, flexible everywhere else
Just to complete the picture for the few that have a UIWebView:
UIWebView -
Struts: top, left, right
Springs: both
UIButton – nothing set, i.e., flexible everywhere
Although my light bulb is dim, there appears to be hope.
My application has a couple views that required a customized navigation bar in the UI for look & feel, however without navigation controller. And the application is required to support iOS version prior to iOS 11, so the handy safe area layout guide could not be used, and I have to adjust the position and height of navigation bar programmatically.
I attached the Navigation Bar to its superview directly, skipping the safe area layout guide as mentioned above. And the status bar height could be retrieved from UIApplication easily, but the default navigation bar height is really a pain-ass...
It struck me for almost half a night, with a number of searching and testing, until I finally got the hint from another post (not working to me though), that you could actually get the height from UIView.sizeThatFits(), like this:
- (void)viewWillLayoutSubviews {
self.topBarHeightConstraint.constant = [UIApplication sharedApplication].statusBarFrame.size.height;
self.navBarHeightConstraint.constant = [self.navigationBar sizeThatFits:CGSizeZero].height;
[super viewWillLayoutSubviews];
}
Finally, a perfect navigation bar looking exactly the same as the built-in one!
Here is the beginning of my response to your update:
Why does the content height of my UIWebView not change with rotation?.
Could it be that because your auto resize doesn't have the autoresizingMask for all directions?
Another suggestion before I come back for this, could you use a toolbar for your needs. It's a little simpler, will always be on the bottom, auto-rotates/positions. You can hide/show it at will etc. Kind of like this: http://cdn.artoftheiphone.com/wp-content/uploads/2009/01/yellow-pages-iphone-app-2.jpg
You may have looked at that option, but just throwing it out there.
Another idea, could you possibly detect what orientation you are rotating from, and just place the button programmatically to adjust for the tab bar. (This is possible with code)
I have used:
let originY: CGFloat = self.navigationController!.navigationBar.frame.maxY
Working great if you want to get the navigation bar height AND its Y origin.
If you want to get the navigationBar height only, it's simple:
extension UIViewController{
var navigationBarHeight: CGFloat {
return self.navigationController?.navigationBar.frame.height ?? 0.0
}
}
However, if you need the height of top notch of iPhone you don't need to get the navigationBar height and add to it the statusBar height, you can simply call safeAreaInsets that's why exist.
self.view.safeAreaInsets.top
Swift : programmatically adding a web view right under the navigation bar
From iOS11 the key to position the view below the navigation bar is to use safeAreaLayoutGuide
From the Apple docs (link):
The layout guide representing the portion of your view that is unobscured by bars and other content.
So in code I will put the top constraint using view.safeAreaLayoutGuide.topAnchor
Again the whole thing will be for example:
import WebKit
class SettingsViewController: UIViewController {
let webView = WKWebView()
let url = URL(string: "https://www.apple.com")
override func viewDidLoad() {
super.viewDidLoad()
configureWebView()
}
override func viewWillAppear(_ animated: Bool) {
follow(url: url)
}
func follow(url: URL?) {
if let url = url {
let request = URLRequest(url: url)
webView.load(request)
}
}
func configureWebView() {
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
}