重新认识UITabBarController,做出新浪微博标签栏

0.055字数 1521阅读 1320

『微信+朋友圈』是现在常见应用社交模式的标准配置,很多应用会选择像新浪微博一样在底部提供一个标签,能够方便快捷的发布状态,那么今天我们来学习如何快速实现这个功能。

新浪微博截图
新浪微博截图

网上的复杂方案

在网上找到的大部分解决方案是这样的

  1. 创建一个标签栏
  2. 给标签栏设置五个ChildViewController。
  3. 创建一个自定义的Button,大小和一个TabbarItem大小一致
  4. 调用 self.View addsubView:方法Button放到第三个TabbarItem的位置遮住它,然后给Button添加事件实现效果。

这样做确实能实现这个功能,但是但我们需要调用poppush方法的时候,这个Button的隐藏和展示就成了麻烦事,特别是当你使用StroyBoard中的导航栏时,每次poppush都会有一个动画效果,这个Button会尤其不协调。当然,你可以通过不断修改逻辑和代码去实现,但是这里给大家介绍一种更简单的方法。


开始前的准备

首先准备一些标签栏用的图标,手边没有素材的可以去[阿里巴巴矢量图]下载一些使用

准备图片

然后创建一个新项目并把图片导入进去

创建项目并导入图片

创建一个继承于UITabbarController的标签栏并作为根视图控制器

创建导航栏

给标签栏添加视图控制器

先给MyTabbarController添加以下方法

- (void)addViewControllerWithTitle:(NSString *)title image:(NSString *)image 
{
UIViewController * vc = [[UIViewController alloc]init];
vc.view.backgroundColor = [UIColor whiteColor];
UINavigationController * nav = [[UINavigationController alloc]initWithRootViewController:vc];
[self addChildViewController:nav];
nav.title = title;
nav.tabBarItem.image = [UIImage imageNamed:image];
}

在ViewDidLoad中调用这个方法

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.tabBar.tintColor = ColorFromRGB(0xEA8010);
[self addViewControllerWithTitle:@"首页" image:@"tab_index"];
[self addViewControllerWithTitle:@"消息" image:@"tab_message"];

UIViewController * vc = [UIViewController new];
[self addChildViewController:vc];
vc.tabBarItem.image = [[UIImage imageNamed:@"tab_release"]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
vc.tabBarItem.imageInsets = UIEdgeInsetsMake(5, 0, -5, 0);

[self addViewControllerWithTitle:@"发现" image:@"tab_discover"];
[self addViewControllerWithTitle:@"我的" image:@"tab_mine"];

}

哦对了,你还需要这个宏来将一个十六进制的颜色转化成UIColor对象

#define ColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

运行看看效果

运行效果
使用标签栏的误区

我们都知道每一个UITabBarItem都有一个image属性和selectedImage属性,分别对应未选中状态下的图片和选中状态下的图片。
我们还知道UITabBar有一个属性叫做tintColor,表示当某个TabBarItem处于高亮状态下时的颜色。
那么他们究竟有什么区别呢?
我们先从上面代码中那句
[[UIImage imageNamed:@"tab_release"]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]说起
这个RenderingMode的参数需要一个UIImageRenderingMode类型的参数,这是一个枚举类型,字面意思为图片渲染模式,它的枚举值如下

UIImageRenderingMode

可以看到,有三个模式,分别为自动,始终原样不动以及始终按照模板渲染,默认是自动。
那么什么是按照模板渲染呢?
这就说到了标签栏处理图片的方式,对于任意一张加载UITabBarItem上的图片,在未选中下是灰色的,无论你使用什么颜色的图片,选中状态下是蓝色的,无论你使用什么颜色的图片。这就是所谓按照模板渲染,标签栏给每一张图片都照着这个过程去渲染,通过改变tintColor就可以改变选中状态下的颜色。而且在默认状态下,如果没有设置selectedImage,那么选中时的图片会使用UITabBarItemimage所使用的图片。

因此,设置一个标签栏只需要设置它每个TabbarItemTitle``image,然后设置TabBartintColor即可。因为即使你在设置选中图片颜色的时候设定参数UIImageRenderingModeAlwaysOriginal,你不还有一个文字颜色要改嘛?还是得设置tintColor的值

那么这个image属性和selectedImage属性还有用吗?
答案是,有用。
记得天猫美团支付宝这些APP每到逢年过节做的那些花里胡哨的界面吗?假设你的标签栏上是四只猴子,未选中就蹲着,选中就站起来,而且猴子的颜色很丰富,这个时候你就会用到imageselectImage,而且记得要把加工模式设置为UIImageRenderingModeAlwaysOriginal

SO,如果你的项目只是一个传统的标签栏,那么问美工要切图的时候就可以跟他说,"你就搞个形状给我就行了,颜色劳资自己搞定╭(′▽`)╯"

回到我们的程序,中间的TabBarItem我们就将其图片的渲染模式设置为UIImageRenderingModeAlwaysOriginal,这样它即使在未选中状态下,依然会是橘黄色。我们不给它加标题,并且调整一下它的位置,这样就有了中间的按钮。

创建发布消息页面

我们首先创建一个发布消息的UIView

创建发布消息的界面

Release.h中这么写
@interface ReleaseView : UIView
@property(nonatomic,strong)UIVisualEffectView * effectView;
@end

Release.m中这样写
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
UIBlurEffect * effrct = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
_effectView = [[UIVisualEffectView alloc]initWithEffect:effrct];
_effectView.frame = self.bounds;
[self addSubview:_effectView];

    UIButton * cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    cancelBtn.frame = CGRectMake(self.bounds.size.width / 5 * 2, self.bounds.size.height - 49 , 40, 40);
    
    cancelBtn.center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height - 49);
    [self addSubview:cancelBtn];
    [cancelBtn setBackgroundImage:[UIImage imageNamed:@"release_cancel"] forState:UIControlStateNormal];
    
    [cancelBtn addTarget:self action:@selector(cancelBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)cancelBtnClicked:(UIButton *)btn
{
self.hidden = YES;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.hidden = YES;
}

UIVisualEffectView是iOS 8.0之后提供的一种用来快速实现半透明效果的视图,通过如上所示的三行代码就可以轻松创建一个半透明的蒙版,它提供三种风格,分别是Light,ExLight和Dark,具体效果大家可以自己试验。

实现最终效果

回到我们的标签栏,将刚才创建的ReleaseView头文件引入
#import "ReleaseView.h"
添加为成员属性
@property(nonatomic,strong)ReleaseView * releaseView;

ViewDidLoad中添加代码

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

....
_releaseView = [[ReleaseView alloc]initWithFrame:self.view.bounds];
_releaseView.hidden = YES;
[self.view addSubview:_releaseView];
....
}

接下来是另一个很重要的步骤,重写setSelectedViewController:(__kindof UIViewController *)selectedViewController方法
- (void)setSelectedViewController:(__kindof UIViewController *)selectedViewController
{
if ([selectedViewController class] == [UIViewController class]) {
_releaseView.hidden = NO;
}
else
{
[super setSelectedViewController:selectedViewController];
}
}

这里我们给其他TabBarItem对应的都是一个导航栏控制器,只有中间这个是个普通的视图控制器,因此可以这么作比较,实际项目中每个标签对应的一定是不一样的视图控制器,因此根据实际情况写即可。

为什么是setSelectedViewController: 不是setSelectedIndex:

有些同学可能注意到了,标签栏有setSelectedViewController:setSelectedIndex:两个方法。那么为什么重写的是前一个呢?
因为当我们主动点击某个标签时调用的就是前一个方法。
那么后一个方法什么时候调用呢?
后一个方法其实是留给开发者使用的一个接口,用来自动跳转到某个标签栏。比如我们可以在调用[self setSelectedIndex:1];让APP一开始就显示第三个标签栏。而这个方法内部其实还是调用了setSelectedViewController:去执行。

运行看效果
运行效果

结束

到此为止,我们已经完成了新浪微博标签栏的制作,至于要往这个半透明的发布页面加什么功能,就凭大家发挥了。

推荐阅读更多精彩内容