I want to make an app, which uses a navigationbar, but just for showing the title and some buttons (it is using a vie container). I want the navigation bar to "fusion" with the status bar, like this
But when I use a generic view controller and drag a navigation bar in it just looks as this:
Is there a way to do this?
I've found a solution for this specific problem. Set the navigation bar's translucent property to NO:
self.navigationController.navigationBar.translucent = NO;
UINavigationController will alter the height of its UINavigationBar to either 44 points or 64 points, depending on a rather strange and undocumented set of constraints. If the UINavigationController detects that the top of its view’s frame is visually contiguous with its UIWindow’s top, then it draws its navigation bar with a height of 64 points. If its view’s top is not contiguous with the UIWindow’s top (even if off by only one point), then it draws its navigation bar in the “traditional” way with a height of 44 points. This logic is performed by UINavigationController even if it is several children down inside the view controller hierarchy of your application. There is no way to prevent this behavior.
Full explanation here.
Not sure whether this will fix your issue, but you can try what i did.
I had the same problem in viewcontroller's right side menu "Simulated Metics" > Top Bar > "Opaque Navigation Bar"
viewcontroller.h file in create iboutlet of the navigation bar
#property (weak, nonatomic) IBOutlet UINavigationBar *navBar;
You can do as followings in viewDidLoad
UIView *addStatusBar = [[UIView alloc] init];
//draw status bar with width of device
addStatusBar.frame = CGRectMake(0, -20, self.view.frame.size.width, 20);
//set the background colour to status bar
addStatusBar.backgroundColor = [UIColor colorWithRed:24.0/255. green:24/255. blue:24.0/255. alpha:1];
[self.view addSubview:addStatusBar];
// add your status bar to your UINavigationBar *navBar which declared in your file .h
[self.navBar addSubview:addStatusBar];
navigationController.navigationBar.clipsToBounds = YES;
In order for UINavigationBar to extend its background under status bar its clipsToBounds must be set to NO (which is the default). Make sure you do not mock around with it.
Solution simple as:-
navigationController.navigationBar.clipsToBounds = NO;
Swift 5
navigationController?.navigationBar.isTranslucent = false
Related
I added a UICollectionViewController using IB. Then I added a UINavigationBar in code:
UINavigationBar *navBar = [[UINavigationBar alloc] init];
[navBar setFrame:CGRectMake(0, 20, self.view.frame.size.width, 44)];
[navBar setDelegate:self];
[self.view addSubview:navBar];
All works fine except the fact that the collection view content is hidden by the navigation bar. It doesn't only scroll beneath the navigation bar but in fact displays beneath it when first loaded.
How can I fix that?
Why are you using a standalone navigation bar? It's probably a bad idea. Embed your view controller in a UINavigationController, and make sure the top contraint of your UICollectionView is set to the top layout guide and not the view.
try changing your content offset of the collection view. I'd try either 44 (height of nav bar) or 64 (height of nav bar + status bar)
Your problem is adding UINavigationBar on self.view which is UICollectionView.
You may use simple UIViewController with UICollectionView as subview of its view. Then after adding UINavigationBar you have to change collectionView.frame.
The iOS7 Facebook App has a right side menu that can be shown by swiping right to left or clicking on the upper right button. When this menu is opened the there is a color transition in the entire status bar from blue to black and vice-versa when closed.
This image shows both status bar side-to-side
This looks like a very good solution for iOS Apps with side menus.
Any ideas or ways about how to accomplish this?
I am currently using JASidePanels.
Thanks!
I managed to find a very simple, elegant way to do this, that mimics the Facebook app functionality perfectly.
Here's my approach:
Create view with status bar frame
Set view background color to black, opacity to 0
Add view as subview to any root view (you need a view that will cover both the center view and the menus, so that it won't be confined to any single view - a good option for this is the container view controller used by your menu controller implementation)
Set view's opacity in your menu controller implementation's menu animation method
Here's my specific implementation, using MMDrawerController:
I subclassed MMDrawerController (I actually already had a subclass for using MMDrawerController with storyboards), and added this code to the class's init method:
// Setup view behind status bar for fading during menu drawer animations
if (OSVersionIsAtLeastiOS7()) {
self.statusBarView = [[UIView alloc] initWithFrame:[[UIApplication sharedApplication] statusBarFrame]];
[self.statusBarView setBackgroundColor:[UIColor blackColor]];
[self.statusBarView setAlpha:0.0];
[self.view addSubview:self.statusBarView];
}
// Setup drawer animations
__weak __typeof(&*self) weakSelf = self; // Capture self weakly
[self setDrawerVisualStateBlock:^(MMDrawerController *drawerController, MMDrawerSide drawerSide, CGFloat percentVisible) {
MMDrawerControllerDrawerVisualStateBlock block;
block = (drawerSide == MMDrawerSideLeft) ? [MMDrawerVisualState parallaxVisualStateBlockWithParallaxFactor:15.0] : nil; // Right side animation : Left side animation
if(block){
block(drawerController, drawerSide, percentVisible);
}
[weakSelf.statusBarView setAlpha:percentVisible]; // THIS IS THE RELEVANT CODE
}];
I also added self.statusBarView as a private property.
The first section of code creates a view, configures it, and adds it as a subview of the MMDrawerController subclass's view. The OSVersionIsAtLeastiOS7() method is a custom method that simplifies the check to see if the device is running iOS 7 (if it isn't, your custom view will show up below the status bar, which you don't want).
The second section of code is MMDrawerController's setDrawerVisualStateBlock method, which sets the animations code to be performed when a menu is being opened and closed. The first few lines of code are boilerplate code that sets one of the prebuilt animations blocks to each menu (I wanted parallax on the left, but nothing on the right). The relevant code is the last line of the block: [weakSelf.statusBarView setAlpha:percentVisible];, which sets the status bar view's opacity to match the percentage that the menu is currently open. This allows for the smooth cross animation you see in the Facebook app. You'll also notice I've assigned self to a variable weakSelf, so as to avoid the "retain cycle" compiler warning.
This is my specific approach using MMDrawerController and a subclass, which I did more for convenience because I already had the subclass in place, than because it is necessarily the best approach or the only way to do it. It could probably be implemented in several other ways, using MMDrawerController without a subclass, or using any other side-drawer menu implementation.
The ending result is a smooth fading to black animation behind the status bar, exactly as you see in the new Facebook app.
I've been trying to accomplish the same thing. The method I am using to do this is based on the following concepts:
A background image with a height of 64 points will fill both the
UINavigationBar and the UIStatusBar.
A background image with a height of 44 points will fill the UINavigationBar and leave the
UIStatusBar black.
You can add an subview to the top of the current navigationController's view and it will sit underneath the UIStatusBar.
So, first, you need to create two images with your desired UINavigationBar look:
A 640x128px image to cover navigation bar and status bar (ImageA)
And a 640x88px image to cover the navigation bar but leave the status bar black (ImageB).
In the application:didFinishLaunchingWithOptions: method, set the background of your UINavigationBar with ImageA with [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"ImageA.png"] forBarMetrics:UIBarMetricsDefault];
When the side menu starts to open, you are going to want switch the UINavigationBar so it uses ImageB and create a view which you will add underneath the UIStatusBar. Here is some sample code for doing just that:
// Add a property for your "temporary status bar" view
#property (nonatomic, strong) UIView *temporaryStatusBar;
And in the code where the side menu starts to open:
// Create a temporary status bar overlay
self.temporaryStatusBar = [[UIView alloc] initWithFrame:[[UIApplication sharedApplication] statusBarFrame]];
self.temporaryStatusBar.backgroundColor = [UIColor yourColor];
[self.navigationController.view addSubview:self.temporaryStatusBar];
// Update both the current display of the navigationBar and the default appearance values
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"imageB.png"] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"imageB.png"] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setNeedsDisplay];
As the side menu animates open, or as the user pans the menu, all you need to do then is adjust the alpha level of the UIStatusBar overlay. When the side menu is fully open, the UINavigationBar should have ImageB as its background image and the UIStatusBar overlay should have an alpha of 0. When the side menu closes, you'll want to replace the UINavigationBar background with ImageA and remove the UIStatusBar overlay.
Let me know if this works for you!
You can use this awesome slide menu library
https://github.com/arturdev/AMSlideMenu
In this demo project you can see how to do that by writing 4 lines of code.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Setting navigation's bar tint color
self.navigationController.navigationBar.barTintColor = [UIColor colorWithHex:#"#365491" alpha:1];
// Making view with same color that navigation bar
UIView *statusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
statusBarView.backgroundColor = [UIColor colorWithHex:#"#365491" alpha:1];
// Replace status bar view with created view and do magic :)
[[self mainSlideMenu] fixStatusBarWithView:statusBarView];
}
I have a problem when dragging a navigation bar or toolbar (storyboard) to my view controller.
UINavigationBar:
As you can see in the image above, the right button is almost overlapping the status bar.
With a UIToolbar it happens the same:
This view controllers are intended to be used as a Modal, that's the reason I'm not using a UINavigationController.
In another section I use a UINavigationController and it appears as I expect:
How can I drag a UINavigationBar / UIToolbar to a view controller without overlapping the status bar?
The navigation bars or toolbars have to be at (0, viewController.topLayoutGuide.length) with bar positioning of UIBarPositionTopAttached. You should set the delegate of your navigation bar or your toolbar to your view controller, and return UIBarPositionTopAttached. If positioned correctly, you will have the result in your third image.
More information here:
https://developer.apple.com/documentation/uikit/uibarpositioningdelegate?language=objc
Do these steps
Drag the NavigationBar to your ViewController in Xib, set the ViewController as its delegate.
Note that the NavigationBar should be at (0, 20)
In ViewController, conform to the UINavigationBarDelegate
#interface ETPViewController () <UINavigationBarDelegate>
Implement this method
- (UIBarPosition)positionForBar:(id <UIBarPositioning>)bar
{
return UIBarPositionTopAttached;
}
positionForBar tells the NavigationBar if it should extend its background upward the Status Bar
Please see my answer here, I've copied the content below for convenience:
https://stackoverflow.com/a/18912291/1162959
The easiest workaround I've found is to wrap the view controller you want to present inside a navigation controller, and then present that navigation controller.
MyViewController *vc = [MyViewController new];
UINavigationController *nav = [[UINavigationController alloc]
initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:NULL];
Advantages:
No mucking with frames needed.
Same code works on iOS 6 an iOS 7.
Less ugly than the other workarounds.
Disadvantages:
You'll probably want to leave your XIB empty of navigation bars or toolbars, and programatically add UIBarButtonItems to the navigation bar. Fortunately this is pretty easy.
You can resolve this issue by using Auto Layout, as per this techincal note (Preventing the Status Bar from Covering Your Views).
Here are some excerpts:
Add the Vertical Space Constraint to the top-most view
Control drag from the UIToolbar to the "Top Layout Guide"
In the popover, choose "Vertical Spacing"
Change the "Vertical Space Constraint" Constant to 0 (zero)
If you have other subviews below the UIToolbar, anchor those views to
the toolbar instead of the Top Layout Guide
This will support ios6 and ios7.
You can also manage it by increasing height of navigation bar by providing image of size 620x128 for ios version. And this image is used in :
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)?YES:NO) {
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"newImage.png"] forBarMetrics:UIBarMetricsDefault];
}else{
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"previousImage.png"] forBarMetrics:UIBarMetricsDefault];
}
I gave up and had to set the navbar height constraint to 64 in x xib based VC cause viewController.topLayoutGuide.length is 0 in viewDidLoad despite statusbar being present :-[
which means in a non universal app on ipad you'd have 20 px on the top of the
view wasted (cause status bar is separate from the iphone simulation window)
In storyboard, in a view controller I tried add a navigation bar under the status bar, running it, it is transparent and shows a label that's supposed to be blurred, like by navigation bar.
But when placing the same view controller embedded in a navigation view controller, the underneath background image could be blurred, which is my intention.
What are these two way different results? What need to do for the firs method to make status bar blur?
Thanks!
In iOS 7 the status bar is transparent by default. The blurring you're seeing when there's also a navigation bar is actually created by the navigation bar. So to create the effect you're looking for without a navigation bar, you need to position a view that produces a blurring effect beneath the status bar.
For reference, add your view with a frame provided by:
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
I know this is old, just for reference, I solved this by setting self.navigationController.navigationBar.clipToBounds = NO
I haven't tested this completely, but go to your plist file and check the following settings:
"View controller-based status bar appearance": If this is set to "Yes", then it should display a status bar that is unique to each View Controller, which might be what you need.
"Status bar style": You may set this to three different styles: Opaque black, Gray, and Transparent black.
Let me know if this worked for you.
UINavigationController will alter the height of its UINavigationBar to either 44 points or 64 points, depending on a rather strange and undocumented set of constraints. If the UINavigationController detects that the top of its view’s frame is visually contiguous with its UIWindow’s top, then it draws its navigation bar with a height of 64 points. If its view’s top is not contiguous with the UIWindow’s top (even if off by only one point), then it draws its navigation bar in the “traditional” way with a height of 44 points. This logic is performed by UINavigationController even if it is several children down inside the view controller hierarchy of your application. There is no way to prevent this behavior.
It looks like you are positioning your view hierarchy in the first example starting at the point (0,20). Also, is that a UIToolbar or a UINavigationBar? If it's the latter, why are you using it by itself and not using it inside of UINavigationController?
If you do not use UINavigationController and are instead using custom view controller containers, you'll need to position your views accordingly.
See this answer for a thorough explanation.
I have similar UI design and based on Matt Hall answer and some article I've googled, I come up with something like this:
- (void)viewDidLoad {
[super viewDidLoad];
if (NSFoundationVersionNumber>NSFoundationVersionNumber_iOS_6_1) {
CGRect statusBarFrame = [self.view convertRect: [UIApplication sharedApplication].statusBarFrame fromView: nil];
UIToolbar *statusBarBackground = [[UIToolbar alloc] initWithFrame: statusBarFrame];
statusBarBackground.barStyle = self.navBar.barStyle;
statusBarBackground.translucent = self.navBar.translucent;
statusBarBackground.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
[self.view addSubview: statusBarBackground];
}
}
Where self.navBar points to navigation bar added in storyboard. This is needed only in case when it runs on iOS7 that is why I've added this condition (my app has to support iOS5).
This works like a charm.
alternative approach (enforce status bar size) is also good:
- (void)viewDidLoad {
[super viewDidLoad];
if (NSFoundationVersionNumber>NSFoundationVersionNumber_iOS_6_1) {
CGRect statusBarFrame = [self.view convertRect: [UIApplication sharedApplication].statusBarFrame fromView: nil];
self.navBar.frame = CGRectUnion(statusBarFrame, self.navBar.frame);
}
}
I've found another solution I think this is best since it involve only storyboard and no code is required.
Switch storyboard view to 6.1 mode (view as: iOS 6.1 and Earlier)
Select problematic UINavigationBar
in size section add 20 delta height in "iOS6/7 Deltas"
Switch back view to 7.0 mode (view as: iOS 7.0 and Later), and be happy with result.
when you embed view controller with navigation view controller that time you will see navigation bar to all the view controller you are pushing to from same view controller. In your first case you are adding the navigation bar object, insted of that you can select view controller from storyboard , go to attributes inspector tab & from their select Top bar as translucent navigation bar.
I have an app where up until now I've been using a UINavigationController with a UINavigationBar that has its property translucent = YES. This means the UINavigationController's content view (i.e. the views from the view controllers you push) to be full-screen (minus status bar).
However, if you set the navigationBar.translucent = NO, this container view becomes 44pt shorter, as I suppose Apple has assumed you don't need any content under an opaque navigationBar.
... except if you're doing what we're doing and are employing a navigationBar that scrolls away (see This Post on how to do that) So I'd like to know if this is possible.
I want to have translucent = NO, but have everything behave as if it were still set to YES. I like the functionality of the translucent = YES, but I don't actually want the bar to be made translucent by UIKit.
What worked for me was to add
extendedLayoutIncludesOpaqueBars = true
in
viewDidLoad
something like this
override func viewDidLoad() {
super.viewDidLoad()
extendedLayoutIncludesOpaqueBars = true
}
Hope it will work for you as well
It's not necessarily a good answer but you could just offset your view that high if you're not translucent.
//This won't take into account orientation and probably other details
if(!self.navigationController.navigationBar.isTranslucent)
{
self.view.frame = CGRectMake(0,0,-44,self.view.bounds.size.height);
}
You could put that in your viewDidLoad or viewWillAppear and if you have a bunch of view controllers you can just subclass them all and put your logic in the subclass.
I found a solution that works, although it is indeed a bit of a hack.
The idea is to give the translucent nav bar an opaque backing. Unfortunately I'm not happy with the solution in that it's dirty and not encapsulated and introduces some potential issues, but i AM happy because it got the job done.
In my Application's base view controller class (i.e. MyViewController : UIViewController), in the viewDidLoad method, I instantiate a new ivar UIView *_navigationBarBG and give it the same frame as self.navigationController.navigationBar. I then set it's backgroundColor property to [UIColor whiteColor] although this is how you achieve some more tint I guess. [EDIT:If you wanted to be a purist (color values remaining exactly as they come from the .psd), you could make the _navigationBarBG a UIImageView and use your custom background there, and the background of the actual UINavigationBar you set to draw clear (or stretch a 1px transparent image if you wanted to use a typical 'change your navigation bar using an image' recipe that's somewhere on the internet)]
if(self.navigationController)
{
_navigationBarBG = [[UIView alloc] initWithFrame: self.navigationController.navigationBar.frame];
_navigationBarBG.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_navigationBarBG];
}
THEN, (and this is the crappy part, but I don't see any other way), I add this view as a subview. BUT, whenever you would normally make a call to [self.view addSubview: anyView], you have to make sure you call [self.view insertSubview: anyView belowSubview: _navigationBarBG];
if (_navigationBarBG)
[self.view insertSubview: anyView belowSubview:_navigationBarBG];
else
[self.view addSubview: anyView];
If you forget that, these added views will slide under your navbar background and look weird. So you need to know that this is a source of error.
WHY AM I DOING THIS? Again you might ask... I want to be able to have a scrolling navigation bar that scrolls out of the way when you scroll down your table view, thereby giving the user more screen space. This is done by using the scrollView delegate (scrollViewDidScroll:) and also viewWillAppear:
// FIRST DEAL WITH SCROLLING NAVIGATION BAR
CALayer *layer = self.navigationController.navigationBar.layer;
CGFloat contentOffsetY = scrollView.contentOffset.y;
CGPoint newPosition;
if (contentOffsetY > _scrollViewContentOffsetYThreshold && self.scrollingNavigationBarEnabled) {
newPosition = CGPointMake(layer.position.x,
22 - MIN((contentOffsetY - _scrollViewContentOffsetYThreshold), 48.0)); // my nav bar BG image is 48.0 tall
layer.position = newPosition;
[_navigationBarBG setCenter: newPosition]; // if it's nil, nothing happens
}
else
{
newPosition = kNavBarDefaultPosition; // i.e. CGPointMake(160, 22) -- portrait only
layer.position = newPosition;
[_navigationBarBG setCenter: newPosition]; // if it's nil, nothing happens
}
I was looking for an answer to this as I wanted my subviews to be at (0,0) and not (0,44)(in reference to the Screen bounds), but I could not find an answer on how to set this in the NavigationController, which I thought would be an included property.
What I ended up doing that was very simple is adding a subview to the navigation controller that was the width and height of the Navigation Bar, but then insert the subview below the Navigation Bar.
Now the setting is Translucent = YES, but it still appears solid and the subviews behave how I want.
EDIT: After re-reading your original post, I suppose if you're going to be rolling the nav bar away, you'll have to take into account hiding and showing the new subview as you do the same with the nav bar