UITabBarItem: Overwrite default appearance in subclass - objective-c

I have a weird problem, let me explain: I use [UITabBarItem appearance] to change the font and color of all UITabBarItems in my app. This works like a charm and all UITabBarItems are styled correctly.
The code is:
// Set the normal state
[[UITabBarItem appearance] setTitleTextAttributes:
#{
UITextAttributeTextColor: AUIColorObject
} forState:forState:UIControlStateHighlighted];
Now I want to overwrite that style for a single UITabBar. I extended my UITabBar subclass to handle something like styles, in my case possible values are RootTabBarControllerStyleDefault and RootTabBarControllerStyleBox.
How can I set the TitleTextAttributes for this single UITabBarItem? I use as subclassed UITabBarController and a UITabBar for maximum control.
More detailed, this is my working workaround:
1) UITabBar has as static method to set the appearance, like this:
+ (void)setAppearinaceForStyle:(RootTabBarControllerStyle)_style
{
[[UITabBarItem appearance] setTitleTextAttributes:
#{
UITextAttributeTextColor: (_style == RootTabBarControllerStyleDefault ? UIColor1 : UIColor2)
} forState:UIControlStateHighlighted];
}
2) Then I set up a delegate for my UITabBarController doing the following stuff:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
[RootTabBar setAppearinaceForStyle:RootTabBarControllerStyleBox];
[tabBarController.tabBar setNeedsDisplay];
for (UITabBarItem *item in tabBarController.tabBar.items)
{
NSString *oldString = item.title;
// Change the title string to force a redraw
[item setTitle:[NSString stringWithFormat:#"%# ", item.title]];
// Set the title back to its default value
[item setTitle:oldString];
}
[RootTabBar setAppearinaceForStyle:RootTabBarControllerStyleDefault];
}
This allows me to set the different style for this single UITabBar but I hope there is a better/cleaner way to do this.

So you want to customize differently one of your UITabBarItem's?
If so, you already have a subclass for that single item, just us [MyTabBarItemSubclass appearance] instead of the regular [UITabBarItem appearance] used for the other items.

The [UIAppearance documentation](
https://developer.apple.com/library/ios/documentation/uikit/reference/UIAppearance_Protocol/Reference/Reference.html) states that:
Use the UIAppearance protocol to get the appearance proxy for a class.
You can customize the appearance of instances of a class by sending
appearance modification messages to the class’s appearance proxy.
Note: iOS applies appearance changes when a view enters a window, it
doesn’t change the appearance of a view that’s already in a window. To
change the appearance of a view that’s currently in a window, remove
the view from the view hierarchy and then put it back.
You should play around with view hierarchy to remove the view that contains the tabbar and putting it again. However, your method is making more or less the same job (a bit less elegantly thus).
I think that te result will be the same if you follow Apple's doc

Related

UIAppearance has no effect on Label text color

I am setting the text color of all the labels in my app using UIAppearance. Yet the text color does not change.
Here is a sample of how i create the label
//show the loading message
MessageLabel *messageLabel = [[MessageLabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
messageLabel.text = #"\n\nLoading ...\n\n";
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = NSTextAlignmentCenter;
[messageLabel sizeToFit];
self.tableview.backgroundView = messageLabel;
Here is how i set the text color
[[MessageLabel appearance] setTextColor:[UIColor blackColor]];
One note is that all these MessageLabel are BackgroundViews of UITableView
As arrteme mentioned, UILabel's textColor doesn't work with UIAppearance. I got around this by adding a UIAppearance compatible property that just wraps textColor. In Swift this would look something like
class MessageLabel: UILabel {
#objc dynamic var configurableTextColor: UIColor {
get {
return textColor
}
set {
textColor = newValue
}
}
...
}
and then to configure the appearance:
MessageLabel.appearance().configurableTextColor = .black
From the Documentation:
iOS applies appearance changes when a view enters a window, it
doesn’t change the appearance of a view that’s already in a window. To
change the appearance of a view that’s currently in a window, remove
the view from the view hierarchy and then put it back.
Referring to this, UIAppearance kind of doesn't really seem work with UILabel...
Since you're subclassing from UILabel, maybe it would make sense to set textcolor property initWithFrame: method on in your MessageLabel class?
Or another option, since you say these MessageLabel instances are used for UITableViewCell's background, maybe it would make sense to leave label's background clear and change background of cell itself, for example in tableView:willDisplayCell:forRowAtIndexPath: method in tableView's delegate?
If you instantiated a UILabel in code, you must manually set textColor property after you set the appearance.
messageLabel.textColor = [[UILabel appearance] textColor];

SKStoreProductViewController title color

How do you change the title color and/or bar tint color in a SKStoreProductViewController?
I'm using the appearance API to set navigation bars to a dark color and the text to white. It changes the title color but not the bar tint color in my SKStoreProductViewController.
I don't think you can. At least not on iOS 7. On iOS 6 you can use the UIAppearance protocol and the SKSPVC will pick up the appearance you set on the UINavigationBar.
As noted on this thread, the SKSPVC is a remote view controller so it's inaccesible programmatically, meaning that you can't set it's appearance directly (or indirectly?).
Do the following to avoid the SKStoreProductViewController to take over a tintColor of value WHITE:
#define kCOLOR_NON_WHITE_COLOR [UIColor darkGrayColor]
// CHANGE ALL TINTING BEFORE WE CREATE An INSTANCE OF THIS BROKEN PIECE
[UIWindow appearance].tintColor = kCOLOR_NON_WHITE_COLOR;
[UIView appearance].tintColor = kCOLOR_NON_WHITE_COLOR;
[UINavigationBar appearance].tintColor = kCOLOR_NON_WHITE_COLOR;
[UIBarButtonItem appearance].tintColor = kCOLOR_NON_WHITE_COLOR;
// NOW CREATE THE THING
SKStoreProductViewController *controller = [[[SKStoreProductViewController alloc] init] autorelease];
This draws all the UIBarButtonItems and the UISegmentedControls in this controller in the defined color AFAIK and thus makes the controller more like your apps design.
IMPORTANT: Just do not forget(!!!) to change all the tinting back after you dismissed this controller, otherwise fresh created views in your app might take over the enforced tinting.
UPDATE: As you might already have found out the following to manipulate the appearance does not work:
[UINavigationBar appearanceWhenContainedIn:[SKStoreProductViewController class], nil]
This fix is for iOS 7 & 8 on iOS 6 you have different issues. =)

Customizing navigationBar in iOS7 - title color not working

I am trying to customize the navigationBar of a navigation controller in iOS7 and the title color is not changing. I am doing the following:
[navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:46.0f/256.0f green:46.0f/256.0f blue:46.0f/256.0f alpha:1]];
[navigationController.navigationBar setTranslucent:NO];
[navigationController.navigationBar setTitleTextAttributes:#{[UIColor whiteColor]:UITextAttributeTextColor}];
[self presentViewController:navigationController animated:YES completion:nil];
The navigationBar translucency gets turned off and it is dark, but the title also stays dark. I've also tried to create a custom label and set it as the title view with not much luck.
How can the title color be changed?
The appearance api continues to evolve, and UITextAttributeTextColor is now replaced with NSForegroundColorAttributeName.
[navigationController.navigationBar setTitleTextAttributes:#{NSForegroundColorAttributeName : [UIColor whiteColor]}];
I'll add two things:
The key comes first, then the object.
If you want to globally change your nav controller's title attributes, use the appearance api:
[[UINavigationBar appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor]}];
I'd put that appearance api call in your app delegate's didFinishLaunchingWithOptions method.
UPDATE: Might as well post the Swift equivalents.
To update the navigationBar for an individual view controller, one can use:
self.navigationController.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]
To change the navigation bar's appearance throughout an entire app, one can use:
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]
Not working for me.
Using UINavigation as a modal view controller.
Apparently title colour needs to be set uiapplication level
Edit:
Best way is to change the navigation title in each VC context:
[self.navigationController.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor], NSForegroundColorAttributeName,nil]];
[navigationController.navigationBar setBarStyle:UIBarStyleBlack];
In swift you'll do the following:
self.navigationController.navigationBar.titleTextAttributes = [
NSForegroundColorAttributeName : UIColor.redColor(),
NSFontAttributeName : UIFont.systemFontOfSize(20)
]
Swift + UIAppearance version:
UINavigationBar.appearance().barTintColor = .blackColor()
UINavigationBar.appearance().barStyle = .Black
The problem in the OP's code is this line:
[navigationController.navigationBar setTitleTextAttributes:#{[UIColor whiteColor]:UITextAttributeTextColor}];
The text color attribute name and value are mixed up. It should be
[navigationController.navigationBar setTitleTextAttributes:#{UITextAttributeTextColor:[UIColor whiteColor]}];
actually looks like a bug. Try
myViewcontroller.title = #"";
navigationController.navigationBar.barStyle = UIBarStyleBlack;
myViewcontroller.title = #"TITLE";
works.
In Swift 4
NSForegroundColorAttributeName was renamed to NSAttributedString.Key.foregroundColor
So If You're looking to change the navigationItem title's color:
let navBarAppearance = self.navigationController!.navigationBar
navBarAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
I used UIColor.white, just insert any UIColor you want the title to be.

How to set appearance for a UIButton without affecting UIBarButtonItems?

Using the following code to customize regular UIButtons also affects UIBarButtonItems and clear buttons in text fields.
[[UIButton appearance] setBackgroundImage:greenButtonImage forState:UIControlStateNormal];
I do not wish to customize the latter elements at all, only regular round rect buttons. I realize using appearanceWhenContainedIn: could be used to set a custom appearance for UIBarButtonItems and UITextField, but i wish for these buttons to remain standard. Subclassing is not an option here as it should not be required for such a simple task.
There is a similar question, but it does not address the issue. Why does -[[UIButton appearance] setBackgroundImage] affect the initial appearance of UIBarItem objects and how do you correct it?
One solution I've used before is to nil out the "backgroundImage" property for UIButtons contained inside of a UINavigationBar:
[[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setBackgroundImage:nil forState:UIControlStateNormal];
This should allow you to customize UIButtons in other cases, without touching the ones inside a UIBarButtonItem in the UINavigationBar.

Custom TabBar on iOS5 with API

I tried the new Customizing API for iOS 5 and have some problems I don`t understand. The way I do it:
UITabBar *tabBar = [rootController tabBar];
if ([tabBar respondsToSelector:#selector(setBackgroundImage:)])
{
[tabBar setBackgroundImage:[UIImage imageNamed:#"tabbar_bg.png"]];
tabBar.selectionIndicatorImage = [UIImage imageNamed:#"over.png"];
tabBar.tintColor = [UIColor colorWithRed:56.0/255.0 green:63.0/255.0 blue:74.0/255.0 alpha:1.0];
tabBar.selectedImageTintColor = [UIColor colorWithRed:94.0/255.0 green:102.0/255.0 blue:114.0/255.0 alpha:1.0];
}
The problem is shown on the image below:
The border ist my Problem... and it only occurs if I try to use it with nice ( :P ) colors.. if I try it with white it looks like this:
Do you have any ideas how to fix it?
If you create a subclass of UITabBarItem and implement the methods
- (UIImage *)selectedImage
- (UIImage *)unselectedImage
You can return whatever images you want from these and they won't have any styling effects applied.
Technically these are private methods, but you aren't calling them, you are overriding them, and I've seen plenty of apps use this technique without being rejected.
You can also use a category to override these methods for all tabbaritems in your app. A good trick is to just override selectedImage to return image, like this:
- (UIImage *)selectedImage
{
return self.image;
}
That way, all of your tab bar items will use whatever image you supply without applying any effects for the selectedImage, but will still use the default grey styling for the unselectedImage. Note that this means that you supply an image with colours for the tab bar items, not just a mask image as normal.