Appearance API - UIBarButtonItem - MPMoviePlayerViewController and Youtube Webview
Asked Answered
P

2

7

I styled a UIBarButtonItem using Appearance API like the following

[[UIBarButtonItem appearance] setBackgroundImage:barButtonBgImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

This works great all over the app. The problem is, that this also changes the buttons in video views of YouTube videos that get loaded if you click a YouTube video in a uiwebview.

YouTube Examples

adding code like this:

[[UIBarButtonItem appearanceWhenContainedIn:[MPMoviePlayerViewController class], nil] setBackgroundImage:barButtonBgImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

doesn't change anything (because it seems the YouTube thing isn't just a MPMoviePlayerViewController.

If I understand correctly, I am also not allowed to change the buttons of the YouTube view (and also I don't want this).

Any ideas how I could stop setting custom bar button images on this YouTube video view?

Here is the sample project if you want to take a closer look: https://dl.dropbox.com/u/80699/BarItemsSample.zip

Petitionary answered 5/9, 2012 at 21:40 Comment(0)
K
2

I believe I have come up with the most efficient solution currently available for this problem. Unfortunately the Youtube video player is of a private class called MPInlineVideoViewController. It is not possible to use the appearance proxy on this class, which would kind of be a hack anyway.

Here is what I came up with. I coded it in a way that it could be used in more than one places and could also be used to solve other Appearance proxy issues, such as the back and next UIBarButtonItems when filling out a form in a UIWebView.

AppDelegate.h

extern NSString * const ToggleAppearanceStyles;

AppDelegate.m

NSString * const ToggleAppearanceStyles = @"ToggleAppearanceStyles";

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    NSNotification *note = [NSNotification notificationWithName:ToggleAppearanceStyles object:NULL userInfo:@{@"flag" : @(YES)}];
    [self toggleAppearanceStyles:note];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleAppearanceStyles:) name:ToggleAppearanceStyles object:NULL];

    return YES;
}
-(void)toggleAppearanceStyles:(NSNotification *)note {

    UIImage *barButtonBgImage = nil;
    UIImage *barButtonBgImageActive = nil;

 if([note.userInfo[@"flag"] boolValue]) {

        barButtonBgImage = [[UIImage imageNamed:@"g_barbutton.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 4, 15, 4)];
        barButtonBgImageActive = [[UIImage imageNamed:@"g_barbutton_active.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 4, 15, 4)];
    }

    [[UIBarButtonItem appearance] setBackgroundImage:barButtonBgImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    [[UIBarButtonItem appearance] setBackgroundImage:barButtonBgImageActive forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

}

MJWebViewController.m

-(void)viewDidAppear:(BOOL)animated {

    [[NSNotificationCenter defaultCenter] postNotificationName:ToggleAppearanceStyles object:NULL userInfo:@{@"flag" : @(NO)}];

    [super viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] postNotificationName:ToggleAppearanceStyles object:NULL userInfo:@{@"flag" : @(YES)}];

    [super viewWillDisappear:animated];
}

In the above code we toggle the appearance styles back to their default values so when the YouTube player is loaded, it uses the default styles. The current ViewController has already loaded so it will have the styled appearance.

When the YouTube player dismisses, the current ViewController will not be reloaded, thus maintaining the styling. When the current ViewController disappears, the styled appearances are turned back on.

Kailyard answered 15/9, 2012 at 1:37 Comment(0)
D
7

Because you are misunderstanding what dose appearanceWhenContainedIn: do.

The SDK document said:

To customize the appearances for instances of a class when contained within an instance of a container class, or instances in a hierarchy, you use appearanceWhenContainedIn: to get the appearance proxy for the class.

The code below dose what you are requiring in you question. Try it before you question me please.

For iOS 5.x, you should make a subclass of UINavigationBar (no any override needed), for example

//In MJAppDelegate.h:
@interface MyNavigationBar : UINavigationBar
@end

//In MJAppDelegate.m:
@implementation MyNavigationBar
@end

Then you should edit your storyboard, let it use MyNavigationBar as its UINavigationController's navigation bar.

Finally, you can use the code below to get what you want:

[[UIBarButtonItem  appearanceWhenContainedIn:[MyNavigationBar class], nil] setBackgroundImage:barButtonBgImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

For iOS 6, you can just use the code below:

[[UIBarButtonItem  appearanceWhenContainedIn:[UINavigationBar class], [UINavigationController class], nil] setBackgroundImage:barButtonBgImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
Dabney answered 15/9, 2012 at 1:52 Comment(5)
in the future, it would be nice if you scaled down your images, before posting. putting full-sized retina images in your answer kind of crowds out everything else on the page. thanks in advance.Henkel
since the images are not important, I just deleted them :)Dabney
you're idea for ios5 is nice, but this goes totally against the appearance api stuff. if i need to subclass every nagivationcontroller i want to style in my app, i could also do it in my subclass. this is really an annoying bug.Petitionary
Please read my answer carefully. You don't need to subclass any of your UINavigationController, but just the UINavigationBar. The UINavigationController is the container who controlling all of your UIViewControllers (No matter how many UIViewControllers it is controlling, there is only ONE UINavigationController and only ONE UINavigationBar). Please make sure you are understanding what dose UINavigationController do correctly.Dabney
i use multiple UINavigationControllers in my Application. So i wanted to style them all using the appearance api. if i need to subclass a UINavigationBar i have to set this subclass to all UINavigationBars of all UINavigationControllers i have. Also, i want theMFMailComposerViewControllers UINavigationBar to be styled, which would not be possible with this UINavigationBar subclassing, but with the direct appearance api call on a UINavigationBar.Petitionary
K
2

I believe I have come up with the most efficient solution currently available for this problem. Unfortunately the Youtube video player is of a private class called MPInlineVideoViewController. It is not possible to use the appearance proxy on this class, which would kind of be a hack anyway.

Here is what I came up with. I coded it in a way that it could be used in more than one places and could also be used to solve other Appearance proxy issues, such as the back and next UIBarButtonItems when filling out a form in a UIWebView.

AppDelegate.h

extern NSString * const ToggleAppearanceStyles;

AppDelegate.m

NSString * const ToggleAppearanceStyles = @"ToggleAppearanceStyles";

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    NSNotification *note = [NSNotification notificationWithName:ToggleAppearanceStyles object:NULL userInfo:@{@"flag" : @(YES)}];
    [self toggleAppearanceStyles:note];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleAppearanceStyles:) name:ToggleAppearanceStyles object:NULL];

    return YES;
}
-(void)toggleAppearanceStyles:(NSNotification *)note {

    UIImage *barButtonBgImage = nil;
    UIImage *barButtonBgImageActive = nil;

 if([note.userInfo[@"flag"] boolValue]) {

        barButtonBgImage = [[UIImage imageNamed:@"g_barbutton.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 4, 15, 4)];
        barButtonBgImageActive = [[UIImage imageNamed:@"g_barbutton_active.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 4, 15, 4)];
    }

    [[UIBarButtonItem appearance] setBackgroundImage:barButtonBgImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    [[UIBarButtonItem appearance] setBackgroundImage:barButtonBgImageActive forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

}

MJWebViewController.m

-(void)viewDidAppear:(BOOL)animated {

    [[NSNotificationCenter defaultCenter] postNotificationName:ToggleAppearanceStyles object:NULL userInfo:@{@"flag" : @(NO)}];

    [super viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] postNotificationName:ToggleAppearanceStyles object:NULL userInfo:@{@"flag" : @(YES)}];

    [super viewWillDisappear:animated];
}

In the above code we toggle the appearance styles back to their default values so when the YouTube player is loaded, it uses the default styles. The current ViewController has already loaded so it will have the styled appearance.

When the YouTube player dismisses, the current ViewController will not be reloaded, thus maintaining the styling. When the current ViewController disappears, the styled appearances are turned back on.

Kailyard answered 15/9, 2012 at 1:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.