自定义UINavigationBar背景

我一直在试图为我的NavigationBar(不只是titleView)设置一个自定义的背景,但一直在挣扎。

我发现这个线程

http://discussions.apple.com/thread.jspa?threadID=1649012&tstart=0

但是我不确定如何实现给出的代码片段。 代码是作为一个新类来实现的吗? 另外我在哪里安装UINavigationController因为我有一个使用NavigationView模板构build的应用程序,所以它不是在我的根控制器中完成的

你只需要像这样重载drawRect:

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

Uddhav和leflaw是对的。 此代码很好地工作:

 @interface CustomNavigationBar : UINavigationBar @end @implementation CustomNavigationBar -(void) drawRect:(CGRect)rect { UIImage *image = [UIImage imageNamed: @"myNavBarImage"]; [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; } @end // this can go anywhere +(UINavigationController*) myCustomNavigationController { MyViewController *vc = [[[MyViewController alloc] init] autorelease]; UINavigationController *nav = [[[NSBundle mainBundle] loadNibNamed:@"CustomNavigationController" owner:self options:nil] objectAtIndex:0]; nav.viewControllers = [NSArray arrayWithObject:vc]; return nav; } 

您必须创buildCustomNavigationController.xib并将UINavigationController放入其中,并将navigationBar类更改为“CustomNavigationBar”。

您必须使用“外观”代理来更改iOS 5.xx中控件(如UINavigationBarUIToolBar等)的背景和其他样式属性。 但是,这些不适用于iOS 4.xx,所以为了向后兼容,您需要混合解决scheme。

如果您要同时支持iOS 4.xx和iOS 5.xx设备(即您的DeploymentTarget是4.xx),则在运行时检查是否存在“外观”select器时,必须小心地调用外观代理或不。

你可以这样做:

 //Customize the look of the UINavBar for iOS5 devices if ([[UINavigationBar class]respondsToSelector:@selector(appearance)]) { [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"NavigationBar.png"] forBarMetrics:UIBarMetricsDefault]; } 

你也应该离开你可能已经实现的iOS 4.xx解决方法。 如果你已经实现了对于iOS 4.xx设备的drawRect解决方法,就像@ludwigschubert所提到的那样,你应该把它放在:

 @implementation UINavigationBar (BackgroundImage) //This overridden implementation will patch up the NavBar with a custom Image instead of the title - (void)drawRect:(CGRect)rect { UIImage *image = [UIImage imageNamed: @"NavigationBar.png"]; [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; } @end 

这将使NavBar在iOS 4和iOS 5设备中看起来相同。

实施一个类别是不可取的。 iOS5可以解决这个问题。 但是对于旧的API,你可以 –

  1. 子类UINavigationBarCustomNavBar并从锂的答案实现自定义drawRect。
  2. 对于所有基于IB的UINavigationControllers ,提供CustomNavBar作为其UINavigationBar自定义类。
  3. 对于所有基于代码的UINavigationControllers 。 使用UINavigationController创build一个XIB并执行第二步。 然后在代码中提供一个工厂方法,从nib加载UINavigationController并提供一个IBOutlet

例如。

 [[NSBundle mainBundle] loadNibNamed:@"CustomNavigationController" owner:self options:nil]; UINavigationController *navController = self.customNavigationController; navController.viewControllers = [NSArray arrayWithObject:controller] 

您也可以重写UINavigationBar类的drawLayer:inContext:方法。 在drawLayer:inContext:方法中,可以绘制要使用的背景图像。

 - (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)context { if ([self isMemberOfClass:[UINavigationBar class]] == NO) { return; } UIImage *image = (self.frame.size.width > 320) ? [UINavigationBar bgImageLandscape] : [UINavigationBar bgImagePortrait]; CGContextClip(context); CGContextTranslateCTM(context, 0, image.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawImage(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage); } 

作为一个完整的演示Xcode项目定制UINavigationBar的外观, 这可能会有所帮助。

在iOS5中实现一个类别将不起作用,您应该使用Uddhav Kambli的build议在iOS上使用CustomNavbar≤5。

我刚刚发现这个博客条目,描述这个主题很简单: http : //web0.at/blog/?p=38

它帮助了我很多,他们使用“drawRect”方法来获得背景的自定义。

对于所有在iOS5中使用UINavigationBar自定义背景的人,请在相应的viewDidLoad方法中执行此操作:

 #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:@"TitleBar"] forBarMetrics:UIBarMetricsDefault]; } #endif 

注意在我的情况下,背景图像被命名为“TitleBar”。 你可以把你的自定义背景图像名称。

您将遇到的问题是,如果您使用导航控制器,每个页面的标题将覆盖您的自定义导航栏。 如果您的导航栏包含徽标或您的应用程序的名称,这显然是不能接受的。

您可以将导航堆栈中的每个视图的标题设置为空白,但是某些视图强制标题不能做任何事情(如照片select器)。 因此,您可能需要使用与徽标导航栏相同的颜色或devise来创build备用导航栏图像,但需要留出一个空白区域来为覆盖标题腾出空间。

要随意切换导航栏图像,请向您的应用程序委托添加一个属性以保存导航栏图像的名称,并将上面第一个示例的第一行replace为以下两行:

  YourAppDelegate* theApp = (YourAppDelegate*)[[UIApplication sharedApplication] delegate]; UIImage* image = [UIImage imageNamed:theApp.navBarName]; 

然后在第一个视图控制器中,您将会推入导航堆栈,执行如下操作:

 - (void)viewWillAppear:(BOOL)animated { YourAppDelegate* theApp = (YourAppDelegate*)[[UIApplication sharedApplication] delegate]; theApp.navBarName = @"navBar_plain"; } 

然后在根视图控制器中,执行相同的操作,但指定带有徽标的导航栏图像,以便在用户导航到导航图像时恢复,并且没有冲突的标题。

另一种方法是使用UINavigationController的委托。 它不需要inheritance/覆盖UINavigationBar类:

 /* in the place where you init the navigationController: fixer = [[AENavigationControllerDelegate alloc] init]; navigationController.delegate = fixer; */ @interface AENavigationControllerDelegate : NSObject <UINavigationControllerDelegate> @end @implementation AENavigationControllerDelegate #define bgImageTag 143 - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { //this is for the future for implementing with the appearance api: if ([[navigationController navigationBar] respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [[navigationController navigationBar] setBackgroundImage:[UIImage imageNamed:@"header-logo-bg.png"] forBarMetrics:UIBarMetricsDefault]; }); } else { UIImageView* imageView = (UIImageView*)[navigationController.navigationBar viewWithTag:bgImageTag]; if(!imageView) { UIImage *image = [UIImage imageNamed:@"header-logo-bg.png"]; imageView = [[[UIImageView alloc] initWithImage:image] autorelease]; imageView.tag = bgImageTag; } [navigationController.navigationBar insertSubview:imageView atIndex:0]; } } @end 

https://gist.github.com/1184147

在iOS5中,( UINavigationBar's最深层)的zPosition值被改变了。 所以如果你改变了zPosition ,那么旧的方法是有效的。

例如。

 UINavigationBar *_bar = navigationController.navigationBar; // Insert ImageView UIImage *_img = [UIImage imageNamed:@"navibar.png"]; UIImageView *_imgv = [[[UIImageView alloc] initWithImage:_img] autorelease]; _imgv.frame = _bar.bounds; UIView *v = [[_bar subviews] objectAtIndex:0]; v.layer.zPosition = -FLT_MAX; _imgv.layer.zPosition = -FLT_MAX+1; [_bar insertSubview:_imgv atIndex:1]; 

这个脚本处理视图的图层,所以你应该导入QuartzCore

下面是一个可供select的解决scheme,可以使用您自己的UINavigationBar的自定义子类:

https://gist.github.com/1253807

正如苹果自己所说,重写类别中的方法是不正确的。 所以定制UINavigarionBar背景的最好方法是UINavigarionBar和覆盖-(void)drawInRect:方法。

 @implementation AppNavigationBar - (void)drawRect:(CGRect)rect { UIImage *patternImage = [UIImage imageNamed:@"image_name.png"]; [patternImage drawInRect:rect]; } 

要使用这个自定义的UINavigationBar它应该被设置为UINavigationBarController navigationBar属性。 你知道这个属性是只读的。 所以应该做的是:

 - (void)viewDidLoad { [super viewDidLoad]; AppNavigationBar *nav = [AppNavigationBar new]; [self setValue:nav forKey:@"navigationBar"]; } 

它适用于iOS 5和4.3。

您可以inheritanceUINavigationBar并像这样启用它,因为drawRect的类别在iOS5中将不起作用

  navigationController = [[((^ { NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:[NSKeyedArchiver archivedDataWithRootObject:navigationController]]; [unarchiver setClass:[SAPHUINavigationBar class] forClassName:@"UINavigationBar"]; [unarchiver setClass:[UIViewController class] forClassName:NSStringFromClass([navigationController.topViewController class])]; return unarchiver; })()) decodeObjectForKey:@"root"] initWithRootViewController:navigationController.topViewController]; 

对于静态视图(根本没有animation),我使用默认的iOS setBackgroundImage

但是,当我有一个animation视图(最有可能resize),我创build一个自定义UIImageView并将其添加到导航栏,以便我有更多的灵活性

事情是,如果你只是添加它,它会得到button和titleView的顶部,所以我手动保存大部分子视图的副本,从父视图中删除它们,添加我的imageView,并添加所有的子视图

这对我有用

  UIImageView *navBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"navigationBackgroundSample.jpg"]]; UIView *tempTitleView = [[self.navigationBar.subviews objectAtIndex:1] autorelease]; [[self.navigationBar.subviews objectAtIndex:1] removeFromSuperview]; [self.navigationBar addSubview:navBackground]; [self.navigationBar addSubview:tempTitleView]; self.navigationBar.clipsToBounds = YES; [navBackground release]; 

在这种情况下,我没有button,我发现我的titleView是在索引1,如果你有button,他们应该在导航条子视图数组中的某处

我不知道是什么在索引0,我不知道这是否可以解决你的案件,你有文字标题既…