02-彩票项目第二季

2.封装SWPTabBar方式一

接着我们思考如何进行封装。前面已经将过了为什么要封装, 和封装达到的效果。这里我们主要有两种封装方式,分别是站在不同的角度上看待问题。虽然角度不同但是内部最核心的思想还是一样的,就是屏蔽控件内部是如何构造的细节, 为外界提供简单容易理解的接口。
方式一: SWPTabBar屏蔽所有内在细节,只需提供要创建的控件个数。就可以创建出SWPTabBar的大体框架。接着有几个方法可以用来设置TabBar内部按钮的数据。简单来说只需提供
1. 设置按钮个数。
2. 设置按钮数据的接口。

注意: 那么我们如何保证接口的健壮性, 要知道如果有用户反复或多次设置按钮个数,那么我们要做到的效果是重新构造TabBar新的内部,而不能造成按钮的重复累加。这也是我们设计接口时候要考虑的一点,该接口是否方便使用, 时候多次使用会出错等等许多细节。并不是所设计接口就是随便的提供个方法的调用这么简单,这需要经验的积累。

首先看下面代码和注释:

-(void)viewDidLoad {
[super viewDidLoad]; // 创建自定义的tabBar控件
SWPTabBar * tabBar = [[SWPTabBar alloc] init]; tabBar.frame = self.tabBar.frame; // 设置控件内部的按钮个数
tabBar.numberOfBarButton = 5; // 控件代理对象, 用于监听对象
tabBar.delegate = self; // 盖在原来tabBar之上
[self.view addSubview: tabBar];
}

看如上代码, 我们的意图很明显,就是用我们自定义的SWPTabBar盖在UITabBar上,造成一种以假乱真的效果。当然你也可以选择把UITabBar删了,在覆盖上去,减少不必要的渲染。接着我们就要关注内部细节的实现。这才是我们要掌握的重点。先看如下主线

分析:
1.首先我们可以选择在initWithFrame中添加控件。为什么不在init呢,因为用户可能会直接调用initWithFrame方法,这样就不正确了。但是init方法其实还会调用initWithFrame方法,所以我们重写initWithFrame方法是最保险的方式。
2.必须在确定了SWPTabBar的尺寸之后再计算其子控件的位置和尺寸, 所以我们重写layoutSubViews方法。

分析中的1和2代码分别如下:

-(instancetype)initWithFrame:(CGRect)frame {

    if (self = [super initWithFrame: frame]) {
[self setup]
} return self;
} -(void)setup { for (int i = 0; i < self.numberOfBarButton; i++) { SWPButton * button = [[SWPButton alloc] init]; // 设置按钮不同状态的图片
[button setBackgroundImage:[UIImage imageNamed:@"TabBar1"] forState: UIControlStateNormal]; [button setBackgroundImage:[UIImage imageNamed:@"TabBar1Sel"] forState: UIControlStateSelected]; // 为每个按钮绑定tag, 方便后面切换为不同的UINavigationController
button.tag = i; // 添加监听
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchDown]; // 设置初始选中按钮
if ( 0 == i) { [self buttonClicked: button];
} [self addSubview: button];
} // 设置断言, 控件个数为按钮个数
assert(self.subviews.count == self.numberOfBarButton);
}

快速的浏览一下这段代码,抓住起重点功能理解即可。为什么我们要抽出setup把添加构造控件内部的代码都放在这,
其一、是为了提高代码的复用。因为现在我们只是在initWithFrame中构造,当我们可用可能会提供xib的方式进行构造,在xib方式中,我们一般在awakeFromNib中进行控件的构造,你可以把它理解为相当于xib的初始化方法。
其二、是为了在出错的时候方便调试,一般一个函数或者方法的功能超过了12行以上不利于后面项目的维护,当然这也不能定死的看。像有的方法,刚好是20行已经是一个功能,而且也不能在抽取,这时候不要死脑筋非得把该方法进行拆分。拆分的是为了复用和调试。代码不难理解,有疑问的可以给我留言。我们主要掌握的是整个的思路。
具体细节需要你们自己进行代码的编写,遇到问题在解决,那才有所意义,不要在意细节。

以下代码为重写layoutSubViews实现内部子控件的位置尺寸的确定。

- (void)layoutSubviews {

    [super layoutSubviews];

    // 按钮的尺寸, Y坐标值
CGFloat buttonW = self.frame.size.width / self.numberOfBarButton;
CGFloat buttonH = self.frame.size.height;
CGFloat buttonY = 0.0; // 遍历按钮,设置尺寸、位置
for (int i = 0; i < self.numberOfBarButton; i++) { CGFloat buttonX = i * buttonW; SWPButton * button = self.subviews[i]; button.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH); button.backgroundColor = [UIColor redColor];
} }

分析:好了运行程序,自定义的SWPTabBar是不是不正常?为什么对应的按钮个数,无法显示出来。仔细思考下,不难发现。我们是在创建完SWPTabBar之后,才来设置按钮的个数。也就是说此时SWPTabBar已经构建后了,我们此时设置的个数根本就没有用!。那么怎么解决,我是通过重写numberOfButtons的setter方法,将setup方法放在setter中。这样,我们就可以再设置按钮个数的时候,再来构造该控件的内部。此时发现了setup的好处没。入果你把这些代码直接方法initWithFrame中, 那么现在就必须先复制,将initWithFrame删除,在粘贴到setter方法中。如果有多个自定义的控件,一个个这么做,我想你会疯了的。
问题:我们需要关注一个细节,什么不是所不要在意细节!该在意的要在意,不该在意的要忽略,要看关注点!
就是如果用户多次调用这个setter方法, 会造成按钮的重复添加。这不是我们要的效果。所以我们需要在这之前移除SWPTabBar内部所有的控件。再进行添加。所以我实现了 clear方法,具体删除细节看代码,思路很简单:逐个遍历,删除。

修改后的代码如下

- (instancetype)initWithFrame:(CGRect)frame {

    if (self = [super initWithFrame: frame]) {

    }

    return self;
} - (void)setNumberOfBarButton:(NSUInteger)numberOfBarButton { _numberOfBarButton = numberOfBarButton; // 先删除存在的按钮
[self clear]; // 先添加按钮
[self setup];
} - (void)clear { [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; }

效果图如下:

点击跳转按钮的效果图

3.改进
我们知道, UITabBarController中的几个子控制器是UINavagationController,当点击下面SWPTabBar控件中的按钮时候
,进行子控制器的切换, 当点击UINavigationController上面的按钮进行跳转时候,实际上是UINavigationController对其子控制器进行压栈操作。现在我们想实现的效果是, 当UINavigationController切换其子控制器的时候, 新的控制器不显示出UITabBar, 我们可以通过SB中操作来勾选新控制器的Hide Bar Buttom …来对UITabBar进行隐藏, 然而我们自定义的SWPTabBar并不是UITabBar的子控件而是其兄弟控件, 所以设置的效果是作用在UITabBar上,又因为SWPTabBar比较后面添加, 盖在了上面, 所以是看不到效果的。
此时我们只需要换种思路, 把SWPTabBar直接加进UITabBar控件中, 并完全覆盖它即可实现这样的效果。

修改的代码如下:(关注第6行和第12行,相信不难理解)

- (void)viewDidLoad {
[super viewDidLoad]; SWPTabBar * tabBar = [[SWPTabBar alloc] init]; tabBar.frame = self.tabBar.bounds; tabBar.numberOfBarButton = 5; tabBar.delegate = self; [self.tabBar addSubview: tabBar]; }

思考:手动 VS 代码

我们的确可以通过勾选Hide Bar Buttom …来隐藏UITabBar,但是当控制器达到一定数量时候, 这种做法的做事效率太低, 这就好比快速排序和冒泡排序,这两种排序在排序个数很少的时候, 冒泡排序取得的时间效率比快速排序还低, 但是当操作某个值后,快速排序有十分好的时间效率。依次类比, 我们也要知道代码的方式, 这样遇到熟练一多的情况, 可以帮助我们提高项目的开发速度。说了太多废话,仁者见仁,智者见智。

    实现具体细节如下:
1.通过自定义的SWPNavigationController重写管理其子控制器栈结构的入栈方法。来拦截push操作
,在push之前, 设置隐藏底部的UITabBar。
2.调用父类的入栈操作,完成父类自己的事情(将UIViewController入栈)。
代码如下:
3.到sb中分别更改UINavigationController的关联类

// SWPNavigationController
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
viewController.hidesBottomBarWhenPushed = YES;
[super pushViewController:viewController animated:animated]; }

改进后点击跳转按钮的效果图

可能你会发现我一直都没讲怎么实现SWPTabBar中如何切换到不同的UINavigationController,这个我们放在下一节介绍,现在我们只需要关注以上的重点,并理解之即可。

    1.自定义类中,继承 + 重写拦截了要调用的方法。
2.UINavigationController与UITabBar管理子控制器的方式
3.如何自定义控件(setup、layoutSubViews)
4.如何更加全面的考虑设计的接口

再造轮子之网易彩票-第二季(IOS 篇 by sixleaves)的更多相关文章

  1. 再造轮子之网易彩票-第一季(IOS 篇 by sixleaves)

    前言 在网上看了别人做的模仿网易彩票的项目, 于是也跟着用自己的想法做了一篇.写这篇博客的目的, 在于UI综合的一次小练习, 同时总结和串联其各个控件之间的应用.封装思想等.考虑到有人上不了githu ...

  2. 小白自制Linux开发板(第二季 V3s篇) 一. 换个核心再来一次

    1.前言 大家心心念念(个人认为)的小白自制开发板全新系列正式来了,之前我们使用全志的F1C200s芯片制作了一个小电脑,众所周知,调试很艰难,坑也很多,以至于墨云到现在还是没找到对应的补救方案,为了 ...

  3. 事件处理原理(IOS篇) by sixleaves

    前言 了解IOS事件处理的本质关键要先掌握几个概念.首先是事件的派发(Event Delivery)的过程, 一个是响应者链条如何构成. 事件的派发: Q1: 你有没有想过,如果你一个屏幕中有多个的V ...

  4. iOS 网易彩票-6设置模块三(常用小功能)

    该篇文章中,用到很多iOS开发过程中常用的小功能,当前只是将这些功能集成到网易彩票的设置中.iOS-常用小功能介绍,请参考我的另一篇文章: iOS 常用小功能 总结:http://www.cnblog ...

  5. iOS菜鸟成长笔记(2)——网易彩票练习

    距离上一篇<第一个iOS应用>已经有一个多月了,今天来和大家一起学习和分享一下一个小练习<网易彩票> 首先我们向storyboard中拖入一个TabBarController和 ...

  6. iOS开发——实战总结OC篇&网易彩票开发知识点总结

    网易彩票开发知识点总结 关于网易彩票开发中遇到了不少的坑,弄了好久才弄懂,或者有些犹豫很久没用就不记得了,所以这里就总结了一下,希望以后不会忘记,就算忘记也能快速查看! /************** ...

  7. QQ聊天界面的布局和设计(IOS篇)-第二季

    QQChat Layout - 第二季 本来第二季是快写好了, 也花了点功夫, 结果gitbook出了点问题, 给没掉了.有些细节可能会一带而过, 如有疑问, 相互交流进步~. 在第一季中我们完成了Q ...

  8. iOS 网易彩票-4设置模块一

    概述 基本上,每一款APP都有相应的设置模块.怎么设置才能更灵活和通用呢,这也是大家一直思考的.下面说说在网易彩票中,设置模块的设置思想. 基本上有三种方案: static cell(呆板,完全没有动 ...

  9. iOS 网易彩票-1框架搭建

    仿网易彩票,最终要做成的效果如下: 一.分层搭建 1.新建一个项目,Lottery.只支持7.1以上坚屏. 2.将素材全部图片全部拉到相应的文件夹里. 3.选中Lottery--右键Show in F ...

随机推荐

  1. Maven--生命周期和插件(四)

    <Maven--搭建开发环境(一)> <Maven--构建企业级仓库(二)> <Maven—几个需要补充的问题(三)> <Maven—生命周期和插件(四)&g ...

  2. javascript笔记7之对象数组

    /* var box = new Array(); //声明一个数组,空数组 alert(typeof box); //数组属于object类型 var box = new Array('李炎恢', ...

  3. poj 2049 Let it Bead(polya模板)

      Description Cannery Row percent of the target audience insists that the bracelets be unique. (Just ...

  4. hdu 5423 Rikka with Tree(dfs)

    Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he ...

  5. 基于css制作轮播图的部分效果

    在轮播图中,我们可以通过鼠标在特定位置上的滑动来实现元素背景的改变.通常在制作轮播图时,我们首先想到的是js中的交互.可是,如果我们无法使用js,只能单纯的靠css又该如何实现这一效果呢?下面,本人将 ...

  6. Android Studio使用技巧系列教程(二)

    尊重劳动成果,转载请注明出处:http://blog.csdn.net/growth58/article/details/46764575 关注新浪微博:@于卫国 邮箱:yuweiguocn@gmai ...

  7. Android自定义控件(一)——开关控件

    Google 在 API 14 开始才新增了Switch 控件. 因此,我们可以选择自己封装一个Switch . 效果如图: View主要代码: public class SwitchView ext ...

  8. UITableView滑动按钮的操作

    一.开题  首先先创建工程, 创建工程的步骤就不一一介绍了, 前面有提过, 接下来是要在VC上创建一个tableview当然你也可以选择一个类继承于UITableView两者都可以, 这要看个人喜欢了 ...

  9. AVD启动不了 ANDROID_SDK_HOME is defined but could not find *.ini

    报错提示______________________________________________________________________ Starting emulator for AVD ...

  10. Chinese_PRC_CI_AS and SQL_Latin1_General_CP1_CI_AS类型错误

    在编写存储过程时,经常会用到临时表,而且往往会使用临时表与正式表相关联. 当临时表与正式表关联的字段是字符类型的时候,因为临时表创建的字符类型会默认数据库的编码,而正式表的字符编码可能不同,那么这个时 ...