iOS 7 中的 UIBarButtonItem 外观问题,这可能是 Apple 的错误吗?
Posted
技术标签:
【中文标题】iOS 7 中的 UIBarButtonItem 外观问题,这可能是 Apple 的错误吗?【英文标题】:UIBarButtonItem appearance trouble in iOS 7, could this be an Apple bug? 【发布时间】:2013-11-15 20:18:36 【问题描述】:不久前我看到一篇文章,在这里:
User Interface Customization in ios 6
它显示了针对 iOS 6 的自定义。自从我写了使用该技术的应用程序的文章以来,它非常简单,没有什么神奇之处。
但是,我需要更新我的一个应用程序,但在 iOS 7 下它无法正常工作。似乎 UIBarButtonItems 的自定义在第一次呈现视图时不起作用。如果我关闭视图然后再次呈现它一切正常。所见即所得:
首次展示:
第二次:
我在他的示例、我的代码和我编写的测试应用程序中看到了这个问题。代码如下:
// 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];
正如您所见,没有真正的魔法,非常标准,但我找不到任何原因或解释为什么这在 iOS 7 中不起作用。代码在 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中执行。
我希望有人看到这一点并可以提供解决方案。感谢您的帮助!
** 注意:有人提出这不是苹果的错误,而是设计使然。我并不是说这是苹果的问题,它更可能是我的问题,但如果您运行任何一个示例或复制并粘贴下面的代码,很明显它第一次无法正常工作,随后又会正常工作。这会让我相信 api 调用是有效的,但要么它们有错误,要么我错过了一些需要做的事情。
**** 更新 4:**
根据 FruityGeek 的建议,我将示例中 MyAppDelegate 的 init 方法中的代码更改为以下代码,但仍然没有运气:
- (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;
**** 更新 3:**
我已将 Dropbox 链接添加到示例项目。这是上面已经发布的链接的补充,该链接是一个简单的应用程序,也显示了问题。两者都可以在 iOS 6 和 iOS 7 的模拟器中构建和运行。在 iOS 6 的情况下,一切都按预期工作。在 iOS 7 中,如果您单击表格单元格并呈现下一个视图,则不会显示自定义后退按钮,如果您返回并再次呈现该按钮,则会出现该按钮。
这几天我一直在开玩笑,所以我希望其他人能看到它并告诉我我错过了什么。
https://www.dropbox.com/s/oi1bh3emvtbmms0/NavigationBarDemo.zip
这可能很愚蠢,但这可能与我的图像有关吗?我将尝试使用不同图像的示例并发布更新。
尝试使用不同的图像并没有区别,还使用了上面发布的示例中的图像。这是一个很长的尝试,但由于似乎没有人有更好的想法,但值得一试。**** 更新 2:**
我已经在另一个测试应用程序中尝试过这个并将代码移动到 init 遇到了应用程序委托,它仍然无法正常工作。我有这个 张贴在这里以及链接的原始作者网站在 最佳。加上另一个论坛,但似乎没有人有解决方案。
我想知道这是否是 Apple 的错误?
**** 更新 1:**
将代码从 didFinishLaunchingWithOptions
移动到 willFinishLaunchingWithOptions
和 init
,但它似乎仍然无法正常工作。
***** 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];
【问题讨论】:
胡思乱想——在视图最初呈现后,你会去外观代理吗?与建立根视图控制器相关的代码在哪里? 我在 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 中执行此操作。在 AppDelegate.m 将您的演示项目放在 Dropbox 上,以便其他人可以查看(并修复代码)。 一只小鸟告诉我你应该用最新的 7.1 beta 重新测试... :-) 有什么解决办法吗? iOS 12 似乎仍然是 2019 年的问题! 【参考方案1】:它看起来确实像 Apple 错误,您应该使用错误报告器在其上输入错误。也就是说,我可以为您提供一个相对轻松的解决方法:将此代码添加到您的 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;
编辑:如果有兴趣,您可以复制这个错误,引用它的错误越多,苹果就越有可能修复它:
错误:15506447
状态:OpenProduct:iOS
2013 年 11 月 19 日下午 3:53
总结:为后栏设置 UIBarButtonItem 外观代理 按钮项目在第二次出现之前没有任何影响 按钮。
重现步骤:在 appDelegate 中,在出现任何内容之前,添加 这些陈述:
UIImage * gradientImage44 = [[UIImage imageNamed:@"navBar_44"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];界面图像 * gradientImage32 = [[UIImage imageNamed:@"navBar_32"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)]; // 放 all UINavigationBars [[UINavigationBar] 的背景图片 外观] setBackgroundImage:gradientImage44 forBarMetrics:UIBarMetricsDefault]; [[UINavigationBar外观] 设置背景图像:渐变图像32 forBarMetrics:UIBarMetricsLandscapePhone];
预期结果:当第一次推送 viewController 时,它的返回 按钮里面有图片。
实际结果:第一次出现,没有图像。推 再次查看并在那里。实际上它确实在您单击时出现 按钮第一次出现,但不是按钮第一次出现时。
版本:Xcode 5.0.1、iOS 7.0.3
注意:在导航的根视图控制器中添加这个 控制器使其工作:
(void)viewDidLoad [超级viewDidLoad];
self.title = @"食谱书"; UIBarButtonItem *it = [[UIBarButtonItem alloc] initWithTitle:self.title 样式:UIBarButtonItemStylePlain 目标:nil 动作:NULL];界面图像 * btBack_30 = [[UIImage imageNamed:@"btBack_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)]; [它 setBackButtonBackgroundImage:btBack_30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; self.navigationItem.backBarButtonItem = 它;
附加的演示项目显示了问题。
配置:
附件:“DynamicsCatalog.zip”已成功上传。
编辑:我很高兴再说一遍,在错误报告器中输入错误有时确实有效!
【讨论】:
是的!我很高兴有人至少认出了他们看到了它!!!!太棒了,谢谢你!!我会尝试你的工作,但它需要进入我的应用程序,所以我会看看它是否有效。实际上我很早就这样做了,但不想这样做,因为我的应用程序中有多个 TableViewControllers。我认为问题出在我使用外观调用的方式上。无论如何,我会试一试,再次感谢!!!! 我尝试了书中的所有技巧,还尝试了一些技巧,但均无济于事。根本问题是Apple在需要时动态创建该按钮,可能是在他们认为不会应用外观的特殊位置。如果没有 swizzling 方法,我想不出任何办法来限制后栏按钮的创建。此外,如果您有多个 tableview 和一个 tabbarController,您可能必须让 tabBarController 在第一次加载每个 tableViewController 时执行此操作 - 或者您可以在其中创建一个小的 UITableView 子类。 天啊,很高兴收到你的来信。我读了又读,我以为我什么都试过了,但快疯了。我很高兴你看到了。我向 Apple 开了一张支持票,所以我会看看它是如何进行的。目前,我使用了您提出的建议并将其放入我的每个 TableViewControllers 中。嘿,快速的问题,也许我的大脑冻结了,但是按钮的标题如何显示为 Back 而不是当前视图的名称。我设置如下:TVCCMSTopLevelMenu * svc; svc.title = homeIconObj.sTitle; 你在某些视图控制器中看到的后退标题实际上是它上面的视图控制器中的后退按钮项!是的,我也对此感到困惑——我已经处理了很多年了。就像当前 vc 的 backBarButton 是从它上面的 viewController 中“借来的”一样。 我在iOS 7 custom back button 上的回答有一个补丁可以修复Apple 显示错误(这是一个错误)。【参考方案2】:我尝试在您的演示项目中更改一些内容,但正如您所说,没有任何效果。 我想可能是因为 UINavigationController 子类,但使用标准的子类具有相同的行为。
不幸的是,如果你真的必须显示你的按钮,在启动时我会默默地做打开关闭的行为......对不起这个丑陋的建议
【讨论】:
你认为这可能是苹果的问题。没有苹果文档说我正在使用的调用在 iOS 7 中不起作用。 很难说。如果您有相同的行为,也许可以尝试使用全新的 7.1 测试版。 与 7.1 的行为相同,经过测试。【参考方案3】:对于 iOS7,您必须以不同的方式自定义后退按钮项。在 iOS6 下,后退按钮是一个带边框的按钮,其中包含前一屏的标题以及在整个按钮下方延伸的背景图像。
在 iOS7 下,Back 控件是一个 V 形图像加上上一屏的标题。如果要使用自定义图像替换默认的 V 形,还需要创建自定义蒙版图像。 iOS 7 使用掩码使前一个屏幕的标题在导航转换期间出现或消失在 V 形中。
因为您是在故事板中执行此操作,所以设置外观代理的最佳位置是在您的应用委托的 init 方法中。
- (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;
【讨论】:
嗯,你读过这个问题吗? “代码在 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 中执行。” did 和 will 方法在应用加载的不同点发生。在您进行 UIAppearance 调用之前,您的视图已经加载(这就是为什么它第二次看起来正确,但不是第一次)。 留下答案的第二部分。 Fruity Geek,我进行了更改并更新了我的帖子……似乎仍然无法解决问题。我在我的应用程序中发布了代码,但我现在也将在我在帖子开头指出的示例中进行尝试。 所以您使用的是故事板或 nib 文件(而不是直接分配rootViewController
。阅读答案的后半部分。【参考方案4】:
我能够通过在视图的 viewWillDisappear 方法中进行后退按钮自定义来解决此问题,该方法正在推送到我想要后退按钮的视图。原因是“拥有”后退按钮的是上一个视图,而不是当前视图。
-(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.
iOS7 外观无需设置背景。虽然如果你愿意,你可以使用自定义栏按钮到自定义导航栏来实现它。
享受编程!
【讨论】:
好的,帮助不大,但无论如何,如果它不是一个错误,无论是苹果还是我,那么为什么它第一次无法工作,第二次按钮出现并工作正确吗?您是否看过上面发布的两个示例下载和/或 init 方法中的代码?以上是关于iOS 7 中的 UIBarButtonItem 外观问题,这可能是 Apple 的错误吗?的主要内容,如果未能解决你的问题,请参考以下文章
UIBarButtonItem 在 iOS 7 的 UIToolbar 上不显示
UIBarButtonItem在用作左侧或右侧导航栏项目时,自定义视图未在iOS 7上正确对齐
如何在 UINavigationBar [iOS 7] 中编辑左、右 UIBarButtonItem 的空白