在iOS 5.0中,UINavigationBar的drawRect方法没有被调用。

19

我已经重载(放置在类别或swizzled中)了UINavigationBar的drawRect以显示自定义背景。在iOS 5中,它不起作用。我应该怎么做?


图像的尺寸应该是多少? - user1266126
7个回答

26

为UINavigationBar设置自定义背景,以支持iOS5和iOS4!

http://www.mladjanantic.com/setting-custom-background-for-uinavigationbar-what-will-work-on-ios5-and-ios4-too/

http://rogchap.com/2011/06/21/custom-navigation-bar-background-and-custom-buttons/


如您所知,直到iOS 5发布前,我们使用drawRect覆盖在AppDelegate中来自定义UINavigationBar。 但是现在,iOS 5为我们提供了一些新的样式方法(旧的不再适用)。

如何构建一个可以在iOS 4和iOS 5上工作并具有样式化的UINavigationBar的应用程序?

您必须两者兼备!

AppDelegate中使用以下代码:

@implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *img = [UIImage imageNamed:@"navbar.png"];
[img drawInRect:rect];
}
@end

在iOS5中,您的视图实现中的viewDidLoad方法中:

if ([self.navigationController.navigationBar respondsToSelector:@selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar.png"] forBarMetrics:UIBarMetricsDefault];
}

如果你看到这里,我们正在询问导航栏是否会响应选择器,以避免在iOS4上崩溃!


这是一个有些不同的解决方案。在这个解决方案中,您应该为每个视图控制器设置导航栏,但同时支持iOS4和iOS5。这样会更好一些。 - tt.Kilew
我把代码放在我的AppDelegate中。每个ViewController都在NavigationBar中有这个图片。看起来你只需要实现一次,而不是在每个ViewController中都实现 :-) - Manni
如果是iOS5设备,您应该将其放置在每个具有新导航控制器的控制器中。 - tt.Kilew
这个提议的解决方案在除了选项卡应用程序中有超过4个选项卡的情况下都可以正常工作。第五个及其后面的选项卡没有进行自定义...有什么想法吗? - JFMartin

12
这里有一个比较好的解决方案,可以在iOS4和5上都适用:
@implementation UINavigationBar (CustomBackground)

- (UIImage *)barBackground
{
    return [UIImage imageNamed:@"top-navigation-bar.png"];
}

- (void)didMoveToSuperview
{
    //iOS5 only
    if ([self respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)])
    {
        [self setBackgroundImage:[self barBackground] forBarMetrics:UIBarMetricsDefault];
    }
}

//this doesn't work on iOS5 but is needed for iOS4 and earlier
- (void)drawRect:(CGRect)rect
{
    //draw image
    [[self barBackground] drawInRect:rect];
}

@end

很棒,当来自类别解决方案并且懒得使用子类技巧时,它非常有效和简单。 - kheraud

11

尝试阅读iOS 5.0发布说明

iOS 5中,UINavigationBar、UIToolbar和UITabBar的实现方式已更改,除非在子类中实现,否则不会调用drawRect:方法。在这些类别中重新实现drawRect:方法的应用程序将发现该方法未被调用。UIKit进行链接检查,以防止在iOS 5之前链接的应用程序中调用该方法,但在iOS 5或更高版本上不支持此设计。


8

以下是可能的解决方案:

最快的解决方法,适用于最懒的人:

@interface MyNavigationBar : UINavigationBar

@end

@implementation MyNavigationBar

- (void)drawRect:(CGRect)rect {

}

@end

@implementation UINavigationBar (BecauseIMLazyHacks)
/*
 Another Ugly hack for iOS 5.0 support
*/
+ (Class)class {
  return NSClassFromString(@"MyNavigationBar");
}

-(void)drawRect:(CGRect)rect {

  CGContextRef context = UIGraphicsGetCurrentContext();
  CGContextTranslateCTM(context, 0, self.frame.size.height);
  CGContextScaleCTM(context, 1.0, -1.0);

  CGContextDrawImage(context, CGRectMake(0, 0, 
  self.frame.size.width, self.frame.size.height), barBackground.CGImage);      

}
@end

虽然这样做是可行的,但你不应该这样做。另外一种方法建议在WWDC'11中提出,是通过覆盖UINavigationBar(创建MyNavigationBar),并像这个链接中的方式从xib初始化UINavigationController:

http://www.iosdevnotes.com/2011/09/custom-uinavigationbars-techniques/

最后,在iOS5.0和iOS5.0-之间使用逻辑流程切换。在可能的情况下使用新API。

类别是错误的路径,交换是错误的路径。(他们只是在你耳边低语:“把自己交给黑暗面。这是你能拯救应用程序的唯一途径。”)


3
drawRect的代码应该放在MyNavigationBar实现中,而不是分类中。 - Matteo Alessani
顺便问一下,barBackground.CGImage是什么?似乎在上面的代码中没有其他地方使用过。 - Selvin
barBackground拥有UIImage类,因此CGIImage只是UIImage类的CGImagRef属性。 - tt.Kilew
除了丑陋之外,这种做法有什么缺点?苹果公司会反对吗? - Sam
目前,苹果公司尚未在应用程序中对此方法做出任何说明,但我被迫使用此方法 :) 但是苹果公司就是这样。你永远不知道他们什么时候会限制某些东西 :) - tt.Kilew

1
@implementation UINavigationBar (MyCustomNavBar)

- (void)setBackgroudImage:(UIImage*)image
{
    CGSize imageSize = [image size];
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, imageSize.height);
    UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:image];
    backgroundImage.frame = self.bounds;
    [self addSubview:backgroundImage];
    [backgroundImage release];
}
@end

上述交换将允许您为UINavigationBar(iOS5和iOS4)设置任何自定义背景图像。

1
很不幸,当你这样做时,按钮会消失。 - Hot Licks

0

请点击链接,使您的代码兼容iOS4、5和6。

您只需在Photoshop或其他软件中制作一个大小为320x44或640x88的矩形(用于Retina显示屏),并将其导入到您的项目中即可。

在AppDelegate中使用以下代码(在#import和@implementation AppDelegate之间的头文件中):

@implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
    UIImage *image = [UIImage imageNamed:@"top_bar.png"];
    [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
@end

在viewDidLoad中,使用以下代码适用于iOS5和iOS6:
#if defined(__IPHONE_5_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_5_0
    if ([self.navigationController.navigationBar respondsToSelector:@selector( setBackgroundImage:forBarMetrics:)]){
        [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"top_bar.png"] forBarMetrics:UIBarMetricsDefault];
    }
#endif

0
在iOS 5之后,当我们为UINavigationBar创建类别时,- (void)drawRect:(CGRect)rect不会被调用,但您可以调用-(void)awakeFromNib并添加所有要添加的代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接