可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to use a custom UINavigationBar
in one of my views, which is not a part of any UINavigationController
-hierarchy. When I drag a UINavigationBar
into Storyboard, it shows like this:
This bar is 44px, which would be enough if the status bar wouldn't share this space. Because iOS7 lets us use the entire screen, including the space for the status bar, the UINavigationBar
should be 64px tall, not 44px.
If I connect the view to a UINavigationController
's hierarchy, then it shows correct:
I read somewhere that if the UINavigationBar
has the property barPosition:
set to UIBarPositionTopAttached
, then the bar would be 64px. This is, however, a readonly
-property.
My search results have only shown something I consider a "work-around". By adding a useless UINavigationController
before this UIViewController
, I can pretend to have a hierarchy, and it will add the UINavigationBar
with 64px automatically.
Is there really no way to have a 'rogue'(without the help of a navigation controller) UINavigationBar
that covers 64px?
If that is the case, is there any valid reasons as to why?
(I'm not saying the UINavigationBar
should be 64px by default, but there should be an option for it in the inspector)
(I also see people answering with programmatic ways to solve this. Even though these answers works, I'd still have to design the storyboard with that in mind (a gap). What I want to know is if it's possible to set this in storyboard, or rather, why isn't it allowed?)
回答1:
You can set the property barPosition
to UIBarPositionTopAttached
another way!
- Add a delegate to your UINavigationBar.
Implement -positionForBar:
in the delegate class:
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar
{
return UIBarPositionTopAttached;
}
Your navigation bar's top must also be anchored to the Top Layout Guide.
回答2:
You can change the nav bar height programmatically:
[navBar setFrame:CGRectMake(0, 0, 320, 64)];
Edit: As I wrote in the comments, you may have to put this line in viewDidLayoutSubviews
to circumvent autolayout.
回答3:
You can set it to attach to the top of the view using 'User Defined Runtime Attributes' in 'Identity Inspector' in Interface Builder. Set the barPosition
property.
Here's the documentation for the barPosition
property: https://developer.apple.com/library/ios/documentation/uikit/reference/UIBarPositioning_Protocol/Reference/Reference.html
回答4:
There is a way to accomplish this WITHOUT using a UINavigationController
or setting it programmatically. Just set a height constraint on the navigation bar to whatever you want, 64 in this instance.
You can accomplish the same thing programmatically as such:
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:navigationBar attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1 constant:64.0f]];
EDIT:
As @nerdist-colony pointed out, when working with Autolayout it's always been to set translatesAutoresizingMaskIntoConstraints
to NO
otherwise there may be conflicts in the constraints.
回答5:
I'm using a standalone UINavigationBar too and I encountered the same issue, and I found there is two ways fixing this properly.
I fixed it using constraints, and a parent UIView. I set this UIView in the storyboard as the parent view of my UINavigationBar with a {0, 0, 320, 64} frame. You can add a constraint or a set of constraints forcing the UINavigationBar to have the desired size.
And given that your parent UIView has the correct frame in your storyboard, you can layout as usual your other views in your storyboard.
Now if autolayout is disabled you can change the bar height programmatically as described in Lindsey's answer.
-(void)viewDidLayoutSubviews
seems to be a good spot to do it.
回答6:
If you using Autolayout, you can set Height of UINavigationBar's constraint to 64 or event more.
回答7:
@startupThekid:
iOS will generate an exception if you try to change autoresizing mask for a UINavigationBar that is managed by a UINavigationController.
However, the Swift equivalent of the objective C code you posted is:
var navBar = self.navigationController!.navigationBar
in viewDidAppear():
navBar.setTranslatesAutoresizingMaskIntoConstraints = false
Wherever you need to set the constraint:
var navBarHeightConstraint =
NSLayoutConstraint(item: navBar, attribute: .Height,
relatedBy: .Equal, toItem: nil,
attribute: .Height, multiplier: 1, constant: height)
self.view.addConstraint(navBarHeightConstraint)
Therafter you should be able to modify the constant property of the constraint as necessary to alter the nav bar height, such as for change in device orientation
回答8:
I've written a blog post detailing how to do this with minimal code intervention. Your bar will show a gap in the storyboard, but it will work fine on the device. In short, the steps are:
1) Add the navigation bar to the view controller in the storyboard.
2) Set the constraints for the navigation bar in the storyboard:
- Navigation Bar.Top = Top Layout Guide.Bottom
- Navigation Bar.Leading = Superview.Leading
- Navigation Bar.Trailing = Superview.Trailing
3) Connect the storyboard's navigation bar to its view controller as an @IBOutlet.
4) In the view controller's class, add a UINavigationBarDelegate protocol to the class declaration.
5) In the view controller's viewDidLoad method, set the navigation bar's delegate to self.
6) In the view controller, add the bar positioning delegate's positionForBar method. Return the UIBarPosition representing TopAttached. (Note: UINavigationBarDelegate inherits this method from UIBarPositioningDelegate.)
The full post with GIF images is here:
Easily use a Navigation Bar without a corresponding Navigation Controller
These steps work as of Swift 2.1/Xcode 7.2
回答9:
In Swift 3 using the main storyboard you can use constraints to change the height and make it work. This was the way that I found is easy to implement.
Creating the constraints.
What it looks like in the end.
回答10:
I was in the same boat and having problems, but now I've fixed it. I'm trying to do the right things to avoid setting the frame explicitly, which is desirable for future-proofing and easy support for different device sizes. In my case, I'm wanting a webview beneath a navigation bar.
I'm setting my view controller to be a UINavigationBarDelegate
and implementing positionForBar:
to return UIBarPositionTopAttached
, like user3269713's answer. It seemed like the other trick was to use auto-layout to position the bar below the topLayoutGuide.
This was the code I was using in viewDidLoad
after creating navigation bar & webview programmatically, and I was still seeing the bar stuck at height 44 but couldn't immediately see why:
[navigationBar sizeToFit];
navigationBar.delegate = self;
[self.view addSubview:navigationBar];
[self.view addSubview:webView];
id guide = self.topLayoutGuide;
NSDictionary *viewNamesDict = NSDictionaryOfVariableBindings(webView, navigationBar, guide);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[webView]|" options:0 metrics:nil views:viewNamesDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[guide][navigationBar][webView]|" options:0 metrics:nil views:viewNamesDict]];
But I did some more thinking and playing around with auto-layout, and it turns out it that alone was the cause of my problem. I was trying to rely on navigation bar's translated constraints from its autoresizing mask, but it turned out I needed to defined them explicitly. Here's my new viewDidLoad
code that works:
[navigationBar sizeToFit];
navigationBar.delegate = self;
navigationBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:navigationBar];
[self.view addSubview:webView];
id guide = self.topLayoutGuide;
NSDictionary *viewNamesDict = NSDictionaryOfVariableBindings(webView, navigationBar, guide);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[navigationBar]|" options:0 metrics:nil views:viewNamesDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[webView]|" options:0 metrics:nil views:viewNamesDict]];
NSString *verticalLayoutVisualExpression = [NSString stringWithFormat:@"V:|[guide][navigationBar(%f)][webView]|", navigationBar.frame.size.height];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:verticalLayoutVisualExpression options:0 metrics:nil views:viewNamesDict]];
And yes, as I'd hoped, this automatically moves the bar when turning to landscape and the status bar hides.
(I heartily recommend a auto-layout helper like Masonry which makes layout code more concise, and Masonry, for one, takes care of turning off translatesAutoresizingMaskIntoConstraints
for you)
回答11:
I know this is a very hacky way of doing this, but for me it worked. I have a NavigationViewController
and I want to load a ViewController modally which has a UINavigationBar
at the top with Done and Cancel buttons. This is what it looked like:
As you can see there is a white band at the top that I want to be the same color as the UINavigationBar
. Instead of changing the height of the UINavigationBar
programmatically, I just made a new view and set its background color equal to that of the UINavigationBar
(HEX = F8F8F8, RGBA = 248,248,248,1.0). Again, I know this is super hacky, but it gets the job done for me. Here is what it looks like now