How do you target particular UIViews when styling its UIAppearance? - iphone

I am setting global styles in iOS 5 with UIAppearance. Here's an example:
[[UIbarButtonItem appearance]
setTtitletextAttributes:someStyle
forState:UIControlStateNormal
];
It looks good in most cases:
But there are cases where the global style looks ugly, like in a movie player.
For the movie player, I would prefer to have the default blue button. So how would I target only the back button's appearance and not the done button's appearance? I have similar issues with targeting normal table cells and grouped table cells.

You need to use a custom subclass of UINavigationBar. Let's call it MyNavigationBar. Then you can do this:
[[UIBarButtonItem appearanceWhenContainedIn:[MyNavigationBar class], nil]
setTintColor:[UIColor redColor]];
and it will only affect buttons within your navigation bar, not the MPMoviePlayerController's navigation bar.
The problem, of course, is that UINavigationController always uses a basic UINavigationBar... if you create it in code. But if you create it in a nib, you can click on its navigation bar (in the nib's document outline) and change the bar's class in the Identity inspector.

If in your AppDelegate you just replace
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"navback"]
forBarMetrics:UIBarMetricsDefault];
with
[[UINavigationBar appearanceWhenContainedIn:[UINavigationController class], nil]
setBackgroundImage:[UIImage imageNamed:#"navback"] forBarMetrics:UIBarMetricsDefault];
movie player will have proper black bar in full screen mode. Works in iOS 5 and iOS 6

You can actually change the proxy to just target certain view hierarchies. In other words, when the view you want to customize is contained in a certain view, you can modify its appearance differently from the rest. So set your global (white) style first, but then call the following method to customize the appearance for when the button appears in an MPMoviePlayerController or whatever else.
[[UIBarButtonItem appearanceWhenContainedIn:
[MPMoviePlayerController class], nil]
setTitletextAttributes:someStyle
forState:UIControlStateNormal];
Let me know if that helps!

Related

Customized navigation controller toolbar is one pixel upper than the bottom of iPhone

I had to customize UIViewController and Used following codes to customize the UIViewController toolbar at the bottom
[self.navigationController.toolbar setBackgroundImage:[UIImage imageWithCGImage:[UIImage imageNamed:#"List/footer.png"].CGImage scale:2 orientation:UIImageOrientationDown] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
[self.navigationController.toolbar setShadowImage:[UIImage imageNamed:#"transparent.png"] forToolbarPosition:UIToolbarPositionAny];
[self.navigationController.toolbar setBackgroundColor:[UIColor clearColor]];
As it is obvious in screenshot picture there is one white pixel under the customized toolbar.
How can I remove the pixel?
It is a simple answer but I have same situation lots of times :) Try to control image List/footer.png it can has 1px transparency bottom of itself?
Another situation is in iOS project I have never give a path like List/
Also my advice is if you want to give a shadow don't use image instead of this use QuartzCore framework and layer.shadow.

UINavigationBar handle rotation

I'm trying to handle the rotation of a UINavigationBar with UINavigationItem (i'm not using the UINavigationController), i have successfully made so that the height and width show according, but that does not seems to be correct, as the UINavigationBar still acts as being in portrait mode (big title and button), also when i set a custom background for both metrics, it seems to only show the portrait background metric, ignoring completely the landscape one.
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"modal-top-landscape"] forBarMetrics:UIBarMetricsLandscapePhone];
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"modal-top"] forBarMetrics:UIBarMetricsDefault];
Is there a way to tell the UINavigationBar that is rotated and should show the text and buttons accordingly?
I attach some screenshots:
One workaround for this is to use a UINavigationController, and use a CGRectOffset to remove the margin that leaves the status bar... add the Controller to the parent controller (also the view).

ABPeoplePickerNavigationController "Groups" View Navigation Controller

I'm using a custom Navigation Bar appearance in my app with this code in the App Delegate's application:didFinishLaunchingWithOptions: method:
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"navBar.png"] forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundColor:[UIColor clearColor]];
However, this appearance breaks when I present an ABPeoplePickerNavigationController (to allow selection of a contact to populate the To: field for a new email), because the system uses an extra tall UINavigationBar when this view is showing due to the prompt property on UINavigationItem being set by the system ("Choose a contact to mail").
The fix is to add this code:
[[UINavigationBar appearanceWhenContainedIn:[ABPeoplePickerNavigationController class], nil] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearanceWhenContainedIn:[ABPeoplePickerNavigationController class], nil] setBarStyle:UIBarStyleBlack];
Which looks like this:
However, this little hack doesn't work when you tap the Groups button from the initial view. It still looks broken due to the extra tall Navigation Bar:
Any ideas what the controller name is for that view (so I can apply the same exclusion as above), or another way to fix this?
I'm just hitting this now.. any luck resolving? I'm thinking to try interating through the ABPeoplepickerNavigationControllers view controllers and applying appearanceWhenContainedIn method...
I've been fighting with this as well, but think I've pieced together a solution.
The group selection view is some other (unknown to me, likely private) class, so we can't specify an exception style through an appearance-proxy-when-contained-in approach, as you've done for the ABPeoplePickerNavigationController. Instead, we should try and resolve the original issue, which is the custom background image not rendering properly when the prompt is shown and the navigation bar is tall.
The solution here (iOS5 UINavigationBar background image issues when prompt is shown) suggests using a resizable background image for the navigation bar.
That almost worked for me, but the background image I was using included the shadow for underneath the navigation bar and that wasn't resizing correctly when the prompt was shown. Instead, I had to use a resizable background image without a shadow and then specify the shadow image separately.
UINavigationBar* navigationBar = [UINavigationBar appearance];
[navigationBar setBackgroundImage:[[UIImage imageNamed:#"TopBarBackgroundNoShadow"] resizableImageWithCapInsets:UIEdgeInsetsMake(3, 0, 3, 0)]
forBarMetrics:UIBarMetricsDefault];
[navigationBar setShadowImage:[UIImage imageNamed:#"TopBarShadowResize"]];
Using this approach removed the need for any exception styling and looked good in both the ABPeoplePickerNavigationController and the group selection view, as well as at standard height.

Why is this bar button black and not clear?

I'm trying to set the tintColor of a UIBarButtonItem in method that gets called when my program starts. I set up all my views using storyboards. I then customize the appearance of the views using the new iOS5 guidelines for using appearance proxies. I've customized the background of the navigation bar by doing the following:
- (void)customizeAppearance
{
UIImage *leatherTexture = [[UIImage imageNamed:#"BrownLeather#2x.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UINavigationBar appearance] setBackgroundImage:leatherTexture
forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:leatherTexture
forBarMetrics:UIBarMetricsLandscapePhone];
[[UIBarButtonItem appearance] setTintColor:[UIColor clearColor]];
}
I was hoping that by setting the UIBarButtonItem tintColor to clear would allow me to easily use the default button styles while having a custom background texture. However, setting the tintColor to clear just turns the button black as opposed to being transparent or clear. Any ideas what I'm doing wrong? Is there a way to create a clear button without having to use custom images for the buttons? See the image below:
You can't do this way (because IMO the default background of UIBarButtonItem is black. and new tint color is over-layered on it).
However you can customize your UIBarButtonItem with using UIButton (with background) as customView in UIBarButtonItem.
Or if you are targeting iOS 5 only you can use brown tint color (which will be flat and will not show background image)
Just found out that in Xcode 4.5 you can just drag a UIButton into the UIBarButton in the Storyboard. The UIButton is then fully customizable.

Why does -[[UIButton appearance] setBackgroundImage] affect the initial appearance of UIBarItem objects and how do you correct it?

When customizing the appearance of UIButton using the class proxy UIBarItems seem to initially take on the custom properties set for UIButton.
Starting with the default Master/Detail project using Core Data. Customize the appearance of UIButton in the AppDelegate and run the app. Click the Edit button, then the Done button in the navigation bar for the MasterViewController and watch the customization go away.
Custom appearance code in [AppDelegate application:didFinishLaunchingWithOptions]:
UIImage *customBackground = [[UIImage imageNamed:#"yourcustomimage.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(5,5,5,5)];
[[UIButton appearance] setBackgroundImage:customBackground forState:UIControlStateNormal];
All UIBarButtonItems initialize with custom background
When the Edit button is replaced by the Done button, it correctly does not have the customized background.
A similar question asks how to customize the Done button. I'm concerned why this is happening at all to UIBarItem objects, which do not inherit from UIButton, and would like to know how to correct it.
I suspect the proxy inheritance and the supported properties, but I don't know how to correct for it. Any suggestions?
My suggestion would be to reset backgroundImage to nil when contained in UINavigationBar:
[[UIButton appearance] setBackgroundImage:customBackground forState:UIControlStateNormal];
[[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setBackgroundImage:nil forState:UIControlStateNormal];