上一篇文章讲了如何搭建以UITabBarController为根视图的界面,虽然是个简单的基础界面,但还是有几个细节问题需要处理。
不知道大家有没有注意到,在setupChildViewControllers
这个函数中设置了selectedImage,但是没有效果。有些应用,比如微博,TabBar中button选中状态是橘黄色的,而不是系统默认的蓝色。这是怎么做到的呢?下面我就来说一说。
button上显示的图片是被渲染过后再显示的,但我们就不希望系统帮我们渲染图片,所以我们需要设置一下了。UIImage有个方法imageWithRenderingMode:
这个方法就可以设置图片渲染方式。这里我们不需要渲染,传个UIImageRenderingModeAlwaysOriginal
即可。因为这个处理方式可能很多地方都会用到,我们不妨给UIImage写一个分类。
//记得在头文件中声明下面两个方法
#import "UIImage+JHExtension.h"
@implementation UIImage (JHExtension)
//成员方法,通过点操作来访问
- (UIImage *)jh_originalImage
{
return [self imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
//静态方法,通过类名访问
+ (UIImage *)jh_OriginalImageWithName:(NSString *)imageName
{
UIImage *image = [UIImage imageNamed:imageName];
[image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
return image;
}
@end
有了这个分类,我们在设置button的时候导入头文件UIImage+JHExtension
,然后我们可以修改上一篇文章中的setupChildViewControllers方法中设置button的selectedImage代码为:
childView.tabBarItem.selectedImage = [UIImage imageNamed:selectedImageName].jh_originalImage;
这样就防止系统渲染图片,从而能显示我们想要显示的图片,而非系统默认的蓝色图片。
通常我们都会在创建UITabBarItem的时候顺带设置下风格,比如字体颜色等。但是每次都需要手动写这些代码会有些麻烦,重复代码多,而且如果要更改的话也比较繁琐。这里我介绍一个方法,UITabBarItem有个appearance,这个就是UITabBarItem的皮肤,如果设置了这个appearance,那么所有UITabBarItem的风格都会根据Appearance来设置。如果只需要设置某些view导航栏的风格,可以用appearanceWhenContainedInInstanceOfClass函数,传入包含需要设置的view的数组就行。只要有UI_APPEARANCE_SELECTOR
宏的UIView都有appearance。话不多说,直接上代码:
/**
* 使用Appearance设置UITabBarItem的风格,这样整个APP中的UITabBarItem都是这个风格
*/
- (void)setUpTabBarItemTitleColor
{
//用字典存储属性和其对应的值
NSDictionary *dic = @{
NSForegroundColorAttributeName: [UIColor darkGrayColor]
};
//取得appearance对象
UITabBarItem *item = [UITabBarItem appearance];
//通过appearance来设置UITabBarItem的属性
[item setTitleTextAttributes:dic forState:UIControlStateSelected];
}
在合适的地方调用这个方法即可。下面来张图看看效果
先来张图
我在这个第一个UITabBarItem对应的ViewController中响应了一个点击完成事件,点击屏幕后push一个绿色的ViewController,你会发现左上角多了一个蓝色的返回按钮,按钮的文本是push绿色ViewController的ViewController的title,而且点击过后这个返回按钮后按钮变淡了。如果我需要自定义这个返回按钮怎么做呢?如果我想整个应用中的返回按钮都使用我自定义的按钮又怎么办呢?下面我们就来解决这个问题。
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//创建ViewController
UIViewController *vc = [[UIViewController alloc] init];
vc.view.backgroundColor = [UIColor greenColor];
//创建一个Button作为返回按钮
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
//设置返回按钮的文本及其颜色
[backButton setTitle:@"返回" forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
//这里的size是我为UIView写的分类,方便直接调用到size
backButton.size = CGSizeMake(40, 40);
//添加按钮响应
[backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
//如果push完view后需要隐藏底部的工具栏,那么可以打开这句
//vc.hidesBottomBarWhenPushed = YES;
//UIBarButtonItem有个initWithCustomView,可以用其它UIView来填充到UIBarbuttonItem中,这样我们就可以把刚才创建的button放在返回按钮所在的位置,替换系统默认的返回按钮
vc.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
//push ViewController
[self.navigationController pushViewController:vc animated:YES];
}
- (void)back
{
//弹出刚才push的view
[self.navigationController popViewControllerAnimated:YES];
}
下面是执行效果,我们可以看到返回按钮已经是我们自定义的了。
2 接下来我们看看如何设置所有被Push的View的返回按钮,并像系统提供的那种一样带箭头。
要让自定义的返回按钮带箭头前,我们必须要了解UIButton的构造。UIButton中,有一个ImageView(左)和一个UILabel(右),我们如果同时为一个UIButton设置了image和title,那么image会显示在button的左半部分,title在button的右半部分。这样我们就比较清楚该怎么设置带箭头的返回按钮了,把图片和文字同时设置好即可。
知道了button怎么设置后,我们要想想如何一劳永逸,只用设置一次就可以改变所有的返回按钮。我们是在push之前设置好view的种种属性,然后push的,我们需要找到一个方法,让所有通过push方式切换的ViewController都能使用我们自定义的返回按钮,能让我们来设置被push的view,怎样做到呢?不知道大家注意到这句代码没 [self.navigationController pushViewController:vc animated:YES];
这里NavigationController的pushViewController方法可以获取到被push的view,那么我们何不重写pushViewController方法,让view被push之前能够做一些设置呢?照这个思路,我们势必是要重写下这个函数了,但问题来了,怎么重写它?很简单,我们自己创建一个类,然后继承于NavigationController后就可以重写这个函数,然后在项目中使用我们自己的NavigationController类就好。废话不多说,直接看代码。
//这个类是继承于UINavigationController的
#import "JHNavigationController.h"
@interface JHNavigationController ()
@end
@implementation JHNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
/**
这里要特别注意,在系统为我们启动应用时,这个pushViewController会被调用,我们不希望程序一启动连最开始的ViewController也有backbutton,所以这个if很重要。
为了自定义push过后的返回按钮,需要拦截pushViewController这个方法来设置。
* 当系统第一次帮我们push的时候,4个view的childViewController.count为0,不会进到if中,所以不管是bottomBar还是leftBarButtonItem都是view自己的设置。如果push页面,此时view的childViewController.count大于0,那么就会进入if设置leftBarButtonItem和bottomBar了
*/
if (self.childViewControllers.count > 0) {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
//设置返回按钮的title,默认title颜色和点击后title的颜色
[backButton setTitle:@"返回" forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
//设置button的大小
backButton.size = CGSizeMake(100, 40);
//设置返回按钮上的 < 箭头
[backButton setImage:[UIImage imageNamed:@"navigationButtonReturn"] forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:@"navigationButtonReturnClick"] forState:UIControlStateHighlighted];
//设置button响应函数
[backButton addTarget:self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
//push完成后隐藏底部工具栏
viewController.hidesBottomBarWhenPushed = YES;
//用backButton来填充UIBarButtonItem
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
}
//!!!!!千万不能忘记调用父类的pushViewController方法,否则就不能正常push了
[super pushViewController:viewController animated:animated];
}
- (void)back:(UIButton *)btn
{
[self popViewControllerAnimated:YES];
}
@end
上面的代码提到了内边距,这里解释了什么是内边距
这一步完成后,在我们之前讲到的JHRootTabBarController的setupChildViewControllers函数中,就可以用我们自己的NavigationController来替换UINavigationController了。来张图看看效果。
//设置返回按钮中内容(button中默认存在的左边的image和右边的label)为居中显示,以调整返回按钮中内容距左边界的距离
backButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
//只靠设置内容居中还不够,我们需要再往左靠,这个时候需要设置内边距
[backButton setContentEdgeInsets:UIEdgeInsetsMake(0, -10, 0, 0)];
我们来看看最终效果!
最后提一点,我们这里用到了PCH文件,这个文件是预编译头文件,是全局的,在这个文件里包含了我写的分类,这样所有应用到分类内容的地方都不需要再导入头文件。PCH文件创建好后需要设置路径。
大功告成! 项目地址点击这里,如果觉得有帮助,希望给个星星