UIBarButtonItem appearance trouble in iOS 7, could this be an Apple bug?

人盡茶涼 提交于 2020-01-01 03:10:51

问题


I saw an article a while back, which is here:

User Interface Customization in iOS 6

It shows customization for iOS 6. Since the article I have written Apps that use the technique, it is pretty straightforward, no magic in there.

However, I need to update one of my apps and under iOS 7 it does not work correctly. It appears that customization of UIBarButtonItems does not work the first time the view is presented. If I dismiss the view and then present it agin everything works fine. What is seen is shown here:

First time view is presented:

Second time:

I have seen this issue in his example, my code and a test app I wrote. The code is as follows:

// Customizing the Back Bar Buttons
UIImage * btBack_30 = [[UIImage imageNamed:@"btBack_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
UIImage * btBack_24 = [[UIImage imageNamed:@"btBack_24"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 5)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

As you can see there is no real magic, pretty standard, but I cannot find any reason or explanation why this does not work in iOS 7. The code gets executed in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.

I hope someone has seen this and can offer a solution. Thanks for any help!

** Note: it was proposed that this is not an apple bug, but by design. I am not saying it IS an apple issue, it could more likely be mine, but if you run either of the sample or copy and paste the code below it is apparent that the first time it does not work correctly and subsequent times it does. That would lead me to believe that the api calls are valid, but either they have a bug, or I am missing something that needs to be done.

**** UPDATE 4:**

I changed the code in MyAppDelegate's init method in my sample to the following based on the suggestion of FruityGeek, but still no luck:

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        //Other UIAppearance proxy calls go here

        [[UIBarButtonItem appearance] setTitleTextAttributes:
         [NSDictionary dictionaryWithObjectsAndKeys:
          //[UIColor colorWithRed:220.0/255.0 green:104.0/255.0 blue:1.0/255.0 alpha:1.0],
          [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0],
          UITextAttributeTextColor,
          //[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0],
          [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8],
          UITextAttributeTextShadowColor,
          [NSValue valueWithUIOffset:UIOffsetMake(0.5, 0.5)],
          UITextAttributeTextShadowOffset,
          [UIFont systemFontOfSize:12.0],
          UITextAttributeFont,
          nil]
                                                    forState:UIControlStateNormal];

        // Customizing the Back Bar Buttons
        //ios6 uses whole button background image
        UIImage * btBack_30 = [[UIImage imageNamed:@"btBack_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
        UIImage * btBack_24 = [[UIImage imageNamed:@"btBack_24"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 5)];
        [[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

        if ([[UIDevice currentDevice].systemVersion integerValue] >= 7)
        {
            //ios7 needs additional chevron replacement image
            UIImage * chevronReplacement = chevronReplacement = [btBack_30 imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
            UIImage * chevronTransitionMaskReplacement = chevronTransitionMaskReplacement = [btBack_30 imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
            [[UINavigationBar appearance] setBackIndicatorImage:chevronReplacement];
            [[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:chevronTransitionMaskReplacement];
        }
    }
    return self;
}

**** UPDATE 3:**

I have added a dropbox link to a sample project. This is in addition to the already posted link above which is a simple app that also shows the issue. Both can be built and run in the simulator with iOS 6 and iOS 7. In the iOS 6 case everything works as expected. In iOS 7, if you click the table cell and present the next view the custom back button is not shown, if you go back and present it again the button is there.

I have been fooling with this for days, so I hope someone else sees it and can tell me what I am missing.

https://www.dropbox.com/s/oi1bh3emvtbmms0/NavigationBarDemo.zip

This may be silly, but could it have to do with my images? I will try the sample with different images and post an update.

  • Tried with different images and that makes no difference, also used images from the above posted sample. It was a long shot, but since no one seems to have a better idea yet it was worth a try.

**** UPDATE 2:**

I have tried this in another test app and moved the code to the init meted of the app delegate and it still does not work. I have this posted here as well as the original authors site of the link at the top. Plus another forum, but no one seems to have a solution.

I am wondering if this could be an Apple bug?

**** UPDATE 1:**

Moved code from didFinishLaunchingWithOptions to willFinishLaunchingWithOptions and init and it still does not appear to work.

***** INIT METHOD FROM AppDelegate.m

- (id)init
{
    // Create resizable images
    UIImage *gradientImage44 = [[UIImage imageNamed:@"navBar_44"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
    UIImage *gradientImage32 = [[UIImage imageNamed:@"navBar_32"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];

    // Set the background image for *all* UINavigationBars
    [[UINavigationBar appearance] setBackgroundImage:gradientImage44 forBarMetrics:UIBarMetricsDefault];
    [[UINavigationBar appearance] setBackgroundImage:gradientImage32 forBarMetrics:UIBarMetricsLandscapePhone];

    // Customize the title text for *all* UINavigationBars
    [[UINavigationBar appearance] setTitleTextAttributes:
     [NSDictionary dictionaryWithObjectsAndKeys:
      [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0],
      UITextAttributeTextColor,
      [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8],
      UITextAttributeTextShadowColor,
      [NSValue valueWithUIOffset:UIOffsetMake(1, 1)],
      UITextAttributeTextShadowOffset,
      [UIFont boldSystemFontOfSize:18.0],
      UITextAttributeFont,
      nil]];

    // Customizing the NavBar Buttons
    UIImage * button30 = [[UIImage imageNamed:@"btButton_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
    UIImage * button24 = [[UIImage imageNamed:@"btButton_24"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
    [[UIBarButtonItem appearance] setBackgroundImage:button30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    [[UIBarButtonItem appearance] setBackgroundImage:button24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

    [[UIBarButtonItem appearance] setTintColor:[UIColor whiteColor]];

    [[UIBarButtonItem appearance] setTitleTextAttributes:
     [NSDictionary dictionaryWithObjectsAndKeys:
      //[UIColor colorWithRed:220.0/255.0 green:104.0/255.0 blue:1.0/255.0 alpha:1.0],
      [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0],
      UITextAttributeTextColor,
      //[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0],
      [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8],
      UITextAttributeTextShadowColor,
      [NSValue valueWithUIOffset:UIOffsetMake(0.5, 0.5)],
      UITextAttributeTextShadowOffset,
      [UIFont systemFontOfSize:12.0],
      UITextAttributeFont,
      nil]
                                                forState:UIControlStateNormal];

    // Customizing the Back Bar Buttons
    UIImage * btBack_30 = [[UIImage imageNamed:@"btBack_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
    UIImage * btBack_24 = [[UIImage imageNamed:@"btBack_24"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 5)];
    [[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    [[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

    return [super init];
}

回答1:


It does look like an Apple bug, and you should enter a bug on it with bug reporter. That said, I can give you a relatively painless workaround: add this code to your RecipetTableViewController:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.title = @"Recipe Book";

    UIBarButtonItem *it = [[UIBarButtonItem alloc] initWithTitle:self.title style:UIBarButtonItemStylePlain target:nil action:NULL];
    UIImage * btBack_30 = [[UIImage imageNamed:@"btBack_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
    [it setBackButtonBackgroundImage:btBack_30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    self.navigationItem.backBarButtonItem = it;
}

EDIT: You can dup this bug if interested, the more bugs referencing it the more likely apple will fix it:

BUG: 15506447

State:OpenProduct:iOS

19-Nov-2013 03:53 PM

Summary: Setting the UIBarButtonItem appearance proxy for the back bar button item does not have any affect until the second appearance of the button.

Steps to Reproduce: In appDelegate, before anything has appeared, add these statements:

UIImage * gradientImage44 = [[UIImage imageNamed:@"navBar_44"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)]; UIImage * gradientImage32 = [[UIImage imageNamed:@"navBar_32"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)]; // Set the background image for all UINavigationBars [[UINavigationBar appearance] setBackgroundImage:gradientImage44 forBarMetrics:UIBarMetricsDefault]; [[UINavigationBar appearance] setBackgroundImage:gradientImage32 forBarMetrics:UIBarMetricsLandscapePhone];

Expected Results: When a viewController is first pushed, its back button has the image in it.

Actual Results: First time it appears, there is no image. Push the view again and its there. Actually it does appear when you click on the button the very first time, but not when the button first appears.

Version: Xcode 5.0.1, iOS 7.0.3

Notes: Adding this in the root view controller of the navigation controller makes it work:

  • (void)viewDidLoad { [super viewDidLoad];

    self.title = @"Recipe Book"; UIBarButtonItem *it = [[UIBarButtonItem alloc] initWithTitle:self.title style:UIBarButtonItemStylePlain target:nil action:NULL]; UIImage * btBack_30 = [[UIImage imageNamed:@"btBack_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)]; [it setBackButtonBackgroundImage:btBack_30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; self.navigationItem.backBarButtonItem = it; }

Attached demo project shows the problem.

Configuration:

Attachments: 'DynamicsCatalog.zip' was successfully uploaded.

EDIT: I'm happy to say that again, entering bugs in bug reporter, does sometimes work!




回答2:


I tried to change a few things in your demo project but as you said nothing works. I thought maybe it's because of the UINavigationController subclass but using a standard one has the same behaviour.

Unfortunately, if you really have to display your button, at launch I would silently do the opening-closing behaviour ... sorry for the uggly proposal




回答3:


You have to handle customizing the back button item differently for iOS7. Under iOS6, the back button was a bordered button that contains the title of the previous screen with a background image that extended under the whole button.

Under iOS7, the Back control is a chevron image plus the title of the previous screen. If you want to use a custom image to replace the default chevron, you also need to create a custom mask image. iOS 7 uses the mask to make the previous screen’s title appear to emerge from—or disappear into—the chevron during navigation transitions.

Because you are doing this in a storyboard, the best place to set the appearance proxies is in your app delegate's init method.

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        //Other UIAppearance proxy calls go here

        // Customizing the Back Bar Buttons
        //ios6 uses whole button background image
        UIImage * btBack_30 = [[UIImage imageNamed:@"btBack_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
        UIImage * btBack_24 = [[UIImage imageNamed:@"btBack_24"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 5)];
        [[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [[UIBarButtonItem appearance] setBackButtonBackgroundImage:btBack_24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

        if ([[UIDevice currentDevice].systemVersion integerValue] >= 7)
        {
            //ios7 needs additional chevron replacement image
            UIImage * chevronReplacement = nil;
            chevronReplacement = [chevronReplacement imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
            UIImage * chevronTransitionMaskReplacement = nil;
            chevronTransitionMaskReplacement = [chevronTransitionMaskReplacement imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
            [[UINavigationBar appearance] setBackIndicatorImage:chevronReplacement];
            [[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:chevronTransitionMaskReplacement];
        }    
    }
    return self;
}



回答4:


I was able to fix this by doing the back button customization in the viewWillDisappear method of the view that is pushing to the view on which I want the back button. The reason is because it's the previous view that "owns" the back button, not the current view.

-(void) viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear: animated];
    UIImage * backButtonImage = [[UIImage imageNamed:@"back.png"] 
                      resizableImageWithCapInsets:UIEdgeInsetsMake(6, 15, 6, 7)];

    UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithTitle:self.title 
                      style:UIBarButtonItemStylePlain target:nil action:NULL];
    [buttonItem setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal
                      barMetrics:UIBarMetricsDefault];
    self.navigationItem.backBarButtonItem = buttonItem;

}



回答5:


This is not the bug. This is default apple's iOS7 behavior.

With iOS7 appearance you don't need to set the background. Though if you want you can achieve it using custom bar button into custom navigation bar.

Enjoy Programming !!



来源:https://stackoverflow.com/questions/20009879/uibarbuttonitem-appearance-trouble-in-ios-7-could-this-be-an-apple-bug

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!