iOS Change NavigationBar appearance throughout entire app - ios

I have multiple UINavigationController throughout my storyboard. Since I am using a tabbarcontroller every tab item has it's own UINavigationController embedded before it's ViewController.
I'd like to style all of these at the same time. Things that I have tried that work are going to a ViewControllers ViewWillAppear method and adding the following lines:
UINavigationBar *nav = self.navigationController.navigationBar;
nav.barStyle = UIBarStyleBlack;
nav.tintColor = [UIColor blackColor];
nav.translucent = NO;
But then I'd have to do this for every tab item and every ViewController.
Also, doing the following in the AppDelegate did NOT work:
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
[[UINavigationBar appearance] setTranslucent:NO];
Specifically I am curious why using the appearance proxy doesn't work. I'm fairly new so if you give a solution involving custom UINavigationController or setting up a delegate please elaborate. Thanks!

Add this line too in your code:
[[UINavigationBar appearance] setBarTintColor:[UIColor blackColor]];

Related

How to hide Navigation Bar back button title from AppDelegate

Whenever my app receives a push notification with custom payload I'd like to push the app to a UIViewController, I was able to do it but I cannot remove the title from the Back button of the UINavigationBar from the AppDelegate.
What I tried was:
[self.window.rootViewController.navigationController setNavigationBarHidden:NO];
[self.window.rootViewController.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
[self.window.rootViewController.navigationController.navigationBar setShadowImage:[UIImage new]];
[self.window.rootViewController.navigationController.navigationBar setTranslucent:YES];
[self.window.rootViewController.navigationController.navigationBar setTintColor:[UIColor mainBlue]];
self.window.rootViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:self.window.rootViewController.navigationItem.backBarButtonItem.style target:nil action:nil];
The last line is where I set the title of the back button to nil but it doesn not work, when the UIViewController becomes visible I can still se the title. The code works if I use it on the UIViewController before that one but not on the AppDelegate. Does anyone have a solution? What am I doing wrong?
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];
This will hide back button title from all navigation items. You should change the offset value based on your title length.

UINavigationBar appearance proxy when contained in UIPopoverController

I'm doing something that supposed to work in iOS7 and 8, but for some reason it doesn't. I want to customize navigation bar properties via appearance proxy and want it to be applied to all navigation bars even the ones that are inside UIPopover.
So, first step I do the following:
UINavigationBar *appearance = [UINavigationBar appearance];
appearance.barTintColor = [UIColor redColor];
appearance.titleTextAttributes = #{
NSForegroundColorAttributeName: [UIColor yellowColor]
};
This supposed to make all navigation bars red with yellow title. Works in iOS8. Mostly work in iOS7. For some reason when view controller is being presented inside UIPopoverController - it gets default appearance.
This is how I present popover (nothing fancy - almost standard sample code):
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"vc2"];
vc.title = #"View Controller 2";
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
self.popover = [[UIPopoverController alloc] initWithContentViewController:nav];
[self.popover presentPopoverFromRect:CGRectMake(100, 100, 100, 100) inView:self.view permittedArrowDirections:0 animated:YES];
Ok, so I decided to try appearanceWhenContainedIn and explicitly set its appearance there. Added the following code to initial appearance customization:
appearance = [UINavigationBar appearanceWhenContainedIn:[UIPopoverController class], nil];
appearance.barTintColor = [UIColor greenColor];
appearance.titleTextAttributes = #{
NSForegroundColorAttributeName: [UIColor blueColor]
};
Now. For some reason this last code doesn't affect anything. In iOS8 navigation bars inside UIPopoverControllers are still red + yellow, not green + blue, and iOS7 still uses default appearance.
What am I doing wrong here?
Here is link to the test project: https://dl.dropboxusercontent.com/u/6402890/TestAppearance.zip
For iOS 8
NS_CLASS_AVAILABLE_IOS(8_0) #interface UIPopoverPresentationController : UIPresentationController
Using the following worked for me. Navigation controller is contained in the UIPopoverPresentationController.
appearance = [UINavigationBar appearanceWhenContainedIn:[UIPopoverPresentationController class], nil];
For iOS 7
appearance = [UINavigationBar appearanceWhenContainedIn:[UIPopoverController class], nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
nav.navigationBar.barStyle = UIBarStyleBlack;
If navigation controller is loaded from storyboard, barStyle is also needed to be set to UIBarStyleBlack in storyboard.

UITabBar setTintColor is not working after [_window setRootViewController]

I am having a weird problem. Whenever my application launches, I do the following in the AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[UINavigationBar appearance] setBarTintColor:[UIColor whiteColor]];
[[UINavigationBar appearance] setTintColor:[UIColor mainLightBlue]];
[[UITabBar appearance] setTintColor:[UIColor mainLightBlue]];
[IJContext setDefaultContext:[[IJContext alloc] init]];
RKLogConfigureFromEnvironment();
return YES;
}
Then, if my user successfully logs in, I do the following in the AppDelegate:
-(void)presentNewsFeed
{
RKLogConfigureByName("RestKit/Network", RKLogLevelDebug);
UIViewController *newTopViewController = [[UIStoryboard storyboardWithName:#"MainiPadStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"GlobalInitialSliding"];
[_window setRootViewController:newTopViewController];
}
At this point, the setTintColor is working fine. Now, whenever a user logs out, I call:
- (void)presentLoginScreen
{
UIViewController *newTopViewController = [[UIStoryboard storyboardWithName:#"MainiPadStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"Login"];
[_window setRootViewController:newTopViewController];
}
Which is again located in the AppDelegate. If the logs back in, I call once again presentNewsFeed. However, on that second time, the setTintColor is no longer working, and all my tabs do not actually have a tintColor whenever their are selected. So my users can't actually know which tab is selected. Any ideas as to why?
I had another declaration of the tintColor in a viewController and it was messing with the one in the appDelegate!
Thanks
Try this code for setting flat red color for tabbar
[[UITabBar appearance] setBackgroundColor:[UIColor redColor]];
for setting image Try this
UIImage *tabBackground = [[UIImage imageNamed:#"tab_bg"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UITabBar appearance] setBackgroundImage:tabBackground];
[[UITabBar appearance] setSelectionIndicatorImage:
[UIImage imageNamed:#"tab_select_indicator"]];
From your question:
Then, if my user successfully logs in, I do the following in the AppDelegate:
-(void)presentNewsFeed
{ ... }
It means your first screen is Login Screen then why do not you set setRootViewController as yourLoginScreenViewController? In your code, You will be also receive a warning such like Applications are expected to have a root view controller at the end of application launch? Because when you will do launch you app at that time your window has not rootViewController so.
And If you follow my suggestion then it is very easy for you to get LoginScreen whenever you LoggedOut, By using following code:
[self.navigationController popToRootViewControllerAnimated:YES];
Because here your RootViewController is your LoginScreen so you directly reach at your RootViewController and also not worry about re-creation of RootViewController.
You can set an image (1px for width and 49 px for height) and save it, for example: tabBg.png, and then you replace:
[[UITabBar appearance] setTintColor:[UIColor mainLightBlue]];
by this:
UIImage *tabBarBackground = [UIImage imageNamed:#"tabBg.png"];
[[UITabBar appearance] setBackgroundImage:tabBarBackground];
I hope this helps you.

UINavigationBar Appearance on Modal Not Setting

I am using the following code in my appDelegate to set the appearance of my UINavigationBar and status bar throughout my app:
[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
[[UINavigationBar appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor]}];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
This code correctly sets the appearance of everything to white everywhere except when a third-party modal viewController is prevented, such as from the Dropbox API or the Mail/Message viewController from a UIActivityViewController. I've included some screenshots to show how these are looking.
UIActivityViewController Mail:
UIActivityViewController Message:
Dropbox API:
I tried putting this in
[[UINavigationBar appearanceWhenContainedIn:[MFMailComposeViewController class], nil] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor]}];
as well as
[[UINavigationBar appearanceWhenContainedIn:[UIActivityViewController class], nil] setTintColor:[UIColor whiteColor]];
and neither one is working.
Like you, I've been trying to alter the appearance of UIActivityViewController and it's "sub" controllers. It seems that in iOS7 the appearance API is somewhat buggy. UIActivityViewController is probably a different process and for sure a separate window, so I'm not really surprised that it's troublesome to style it.
Anyway I found an interesting way around this issue, but your designers might not like it. Create a subclass of UIWindow (ex: MyWindow), instantiate it as your main window and every time you use appearance API use it like this:
[UINavigationBar appearanceWhenContainedIn:[MyWindow class], nil].barTintColor = [UIColor redColor];
This way you'll only style views that actually belong to your application and the Apple-provided views will remain white/blue. I guess it's not the solution you were looking for, but on the other hand it gives users a good understanding what is your app and what is system-provided ;)
In iOS 8 the UIActivityViewController presents its individual compose controllers on the root view controller of your application.
You need to subclass your root view controller (whether it be a UIViewController or UINavigationController) and add the following code.
#interface UINavigationControllerBarColor : UINavigationController
#end
#implementation UINavigationControllerBarColor
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
[super presentViewController:viewControllerToPresent animated:flag completion:^{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
if (completion) {
completion();
}
}];
}
#end
and then instead of initializing a UINavigationController in the AppDelegate or storyboard, initialize your newly subclassed controller.
Some other recommendations subclass the UIActivityViewController but this does not work.
If you want to change the bar button and title colors as well use the following in your application:didFinishLaunching:
[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
[[UINavigationBar appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
[UIColor whiteColor], UITextAttributeTextColor,
[UIFont systemFontOfSize:18.0f], UITextAttributeFont,
nil]];
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:[UIColor whiteColor]];
I've been struggling with this same issue for hours and my conclusion is that all activities from the UIActivityViewController may have their own style implementation and will look depending on that.
Basic problem: You can customise something to look ok for mail and messages, but other apps may look wrong. i.e: Facebook Messanger for some reason forces status bar to be light.
My recommendation: Create a subclass of UIWindow, use that subclass within your application and target UIAppearance to that window class and let the system's interfaces to just be :), (or with minor changes like tint color).
#interface MyWindow : UIWindow
#end
// further in code
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:#[[MyWindow class]]] setTintColor:[UIColor orangeColor]];
FYI: We have literally no control over status bar when using UIActivityViewController
Hope this helps.
Since there is no solution until now, I did the following to set the color of navigation bar of UIActivityViewController modal to white (as my app's navigation bar color is blue) so that the users can at least see the buttons:
[[UINavigationBar appearance] setBarTintColor:[UIColor whiteColor]];
When the user is done with the UIActivityViewController modal, the app's main navigation bar color is returned to blue.
Hopefully somebody will post a better solution.
I found a solution to change the text color of the Send and Cancel buttons.
Check my answer from here.
Try this:
[[UINavigationBar appearance] setBarTintColor:[UIColor whiteColor]];
I have the same problem and I used ActivityViewController's completion handler delegate to set back my bar Tint color to white with this line :
shareViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
[[UINavigationBar appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName:[UIColor whiteColor]}];
};
However this doesn't work anymore on iOS 8... They changed little bit the completion handler format, the code got executed but the color didn't change.
So in order not to waste all of my time here is my quick fix :
I am still changing the global color with this line just before showing the sharing controller (with comment for maintenance)
[[UINavigationBar appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName:[UIColor darkGrayColor]}]; // Note : In ViewWillAppear the color is set back to white
In each view controller that are calling a UIActivityViewController, I am setting in the viewWillAppear method the code to get the color back.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[UINavigationBar appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName:[UIColor whiteColor]}];
}
This works good although it produce lack of cohesion in the code.
I used walapu's answer to make my own solution. The point is, that I set up navigation bar tint color also in presentViewController:animated:completion: and not using appearance proxy, but directly for MFMailComposeViewController.
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)animated completion:(void (^)(void))completion
{
if ([viewControllerToPresent isKindOfClass:[MFMailComposeViewController class]]) {
[((MFMailComposeViewController *)viewControllerToPresent).navigationBar setTintColor:[UIColor whiteColor]];
}
[super presentViewController:viewControllerToPresent animated:animated completion:^{
if ([viewControllerToPresent isKindOfClass:[MFMailComposeViewController class]]) {
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
if (completion) {
completion();
}
}];
}
I had a problem in particular with iMessage. Had to set navbar background image, setting the tint didn't work. Used slicing to stretch a 2x2 pixel image with my color.
[UINavigationBar.appearance setBackgroundImage:[UIImage imageNamed:#"red"]
forBarMetrics:UIBarMetricsDefault];
Here I change the nav bar's global appearance immediately before presenting the activity view, and change it back once the activity view is dismissed. (Tested on iOS 12, Swift 5)
let activityVC = UIActivityViewController...
// Temporarily change the nav bar button's tint color.
let originalColor = UINavigationBar.appearance().tintColor
activityVC.completionWithItemsHandler = { type, completed, items, error in
UINavigationBar.appearance().tintColor = originalColor
}
UINavigationBar.appearance().tintColor = UIColor.blue
present(activityVC, ...

UIAppearance Remove Custom NavBar Background for MFMailComposeViewController

I have a custom navBar image in the navigation controllers in my app, set using UIAppearance protocol. However, when sending mail through the app (via MFMailComposeViewController), I want the default navBar instead of the custom one. I tried the approach outlined in this question: UIAppearance Remove Custom NavBar Background for UIPopoverController but it did not work. The code I used was:
[[UINavigationBar appearanceWhenContainedIn:[MFMailComposeViewController class], nil] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
But it had no effect whatsoever. My app is iOS 6+. Is this something specific to MFMailComposeViewController or am I missing something from this?
Edit: other approaches attempted:
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
mailer.navigationBar.barStyle = UIBarStyleBlack;
[self.navigationController presentViewController:mailer animated:YES completion:nil];
[mailer.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
Setting UIBarStyleBlack has some effect as the "Cancel" button subsequently turns black, but the background image is still set at the old value.
Try something like this:
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
[mail.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
This should reset the background image for just this instance.
Remove custom background image
[[UINavigationBar appearance] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
BEFORE calling,
MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
Point is to set any customization for Navigation Bar appearance before init.

Resources