I'm trying to set a horizontal gradient on a UINavigationBar. I've so far got it so that in portrait mode the gradient displays correctly by using the following code:
static internal class UINavigationControllerExtensions
{
public static void SetBarDefaults(this UINavigationController controller)
{
controller.NavigationBar.BarTintColor = UIColor.FromPatternImage(UIImage.FromFile(IosConstants.ApplicationGradient));
//Other setup stuff
}
}
This extension method gets called on our first view and it sets it correctly for all the portrait views.
My problem occurs when the device is rotated. When this happens the gradient gets cut off at the width of the NavigationBar as if it is in portrait mode and then starts again resulting in a line of stark difference.
I've tried as an experiment waiting for a rotation event on a view controller then re-calling this method to try to re-draw the gradient but apparently that doesn't work either.
Does anyone know how I can get this to resize properly on rotation?
You can use that :
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"gardientImage"] forBarMetrics:UIBarMetricsDefault];
for ios 7 navigation bar height is 64px
Related
It seems to be a feature on iOS 8 that the status bar is hidden when in landscape mode. One can get the status bar back, which is fine, but I actually have no objection to it being hidden except that the navigation bar in my app has the top few pixels cut off, as you can see in this simulator screenshot:
See how the gear icon is right up against the top of the screen? It should have a little more breathing room than that.
It's better if I restore the status bar, but I wouldn't mind leaving it out if I could get the navigation bar to have a little more space. Studying the Messages app on iOS 8, I see that the nav bar height is the same as what I'm seeing, but the compose button gets smaller. How do I update my app to reduce the size of the navigation bar items in landscape?
Turns out that I had been setting the font size of that settings button (which just has U+2699 (⚙) for its title) in -viewDidLoad:
UIBarButtonItem *settingsButton = self.navigationItem.leftBarButtonItem;
[settingsButton setTitleTextAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:28]}
forState:UIControlStateNormal
];
That's why it was too big in horizontal orientation on iOS 8, though just right in portrait.
To fix this issue, I deleted this code from -viewDidLoad and added two new methods to handle resizing it:
- (void)setSettingsButtonFontSize:(CGFloat)size {
UIBarButtonItem *settingsButton = self.navigationItem.leftBarButtonItem;
[settingsButton setTitleTextAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:size]}
forState:UIControlStateNormal
];
}
- (void)setSettingsButtonFontSizeForVerticalSizeClass:(UIUserInterfaceSizeClass)sizeClass {
[self setSettingsButtonFontSize:sizeClass == UIUserInterfaceSizeClassCompact ? 20 : 28];
}
The first method merely sets the specified font size; the second selects a size for based on a size class parameter.
To get it to size properly, I added this code to -viewWillAppear::
if ([self respondsToSelector:#selector(traitCollection)]) {
[self setSettingsButtonFontSizeForVerticalSizeClass:self.traitCollection.verticalSizeClass];
} else {
[self setSettingsButtonFontSize:28];
}
So I get the original behavior on iOS 7, but on iOS 8, the icon gets sized to 20 when the vertical size class is compact.
And finally, I added the new-to-iOS-8 method to handle rotations:
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
[self setSettingsButtonFontSizeForVerticalSizeClass:newCollection.verticalSizeClass];
}
So now, on iOS 8 on the iPhone, the gear is 20 in landscape orientation and 28 in portrait. And now it looks good in landscape, not too crowded:
You might be adjusting frames in the wrong place. I recommend setting all frames (i.e. the frame of the gears) in the viewDidLayoutSubviews method.
I used this following code to set image for navigation title but when i checked it on landscape the image is coming out of navigation bar.I tried resizing but its not working.thanks
UIImageView *image=[[UIImageView alloc]initWithFrame:CGRectMake(0,0,45,45)] ;
[image setImage:[UIImage imageNamed:#"titleimage.png"]];
[self.navigationController.navigationBar.topItem setTitleView:image];
In landscape the navigation bar is 32 points high rather than 44. So, in both orientations your image view is too big really. But in landscape you should be replacing the image view with a new instance with a smaller frame height.
Resize or change the UIImageView size when device going to change orientation, implement this delegate method
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
//You can replace image view with new, or you can set the frame of already existing image view in here. height should be 32 for iPhone
}
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'm trying to make a custom UINavigationBar, but the problem is that my background image has a curve in it, and its little bit larger than default navigation bar. When I try to set background image and change navigation bar size, its just scales image as a rectangle. Any ideas?
Bar:
Override
- (CGSize) sizeThatFits:(CGSize)size {
return CGSizeMake(custom_width, custom_height);
}
in order to return the size for your custom navigation bar.
Note that if you use a height that is not a multiple of 4, it will cause trouble if you hide and then show the navigation bar at any point (it gets shifted by 1 pixel from the top)
When using Apple's UISplitViewController, the master view has a black toolbar when in portrait mode and gray when in landscape.
I have created a label inside a UIView on the toolbar, as described here: Adding a UILabel to a UIToolbar, but by default the text is black which means it is only visible in landscape mode.
How should I code my view controller so it can change the text color so it is always visible, and ideally to match the way Apple apps like Mail work (white in portrait and dark gray in landscape) ?
...later...
Following the direction in Alan Moore's answer, I decided to use code like this:
UIInterfaceOrientation o = [UIApplication sharedApplication].statusBarOrientation;
if (UIDeviceOrientationIsPortrait(o))
label.textColor = [UIColor whiteColor];
else
label.textColor = [UIColor darkTextColor];
This is called from my viewDidLoad and didRotateFromInterfaceOrientation methods. Seems to me that didRotate is better than shouldRotate for this case.
Also note I am querying the statusBar, because in my view self.interfaceOrientation always returns 1. This is also noted here: Determine UIInterfaceOrientation on iPad.
Not 100% sure that darkText is the right color for landscape.
I would add code to the
- (BOOL)shouldAutorotateToInterfaceOrientation:(...) {
}
which would do a setTextColor on your label depending on whether it's rotating to portrait or landscape mode.