自定义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中控件(如UINavigationBar
, UIToolBar
等)的背景和其他样式属性。 但是,这些不适用于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,你可以 –
- 子类
UINavigationBar
说CustomNavBar
并从锂的答案实现自定义drawRect。 - 对于所有基于IB的
UINavigationControllers
,提供CustomNavBar
作为其UINavigationBar
自定义类。 - 对于所有基于代码的
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
在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的自定义子类:
正如苹果自己所说,重写类别中的方法是不正确的。 所以定制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,我不知道这是否可以解决你的案件,你有文字标题既…