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. qt动态更新界面的菜鸟代码,请指出

    qt简单界面更新代码(菜鸟级)(部分代码) self.timers_1=QtCore.QTimer(self) self.timers_1.timeout.connect(self.min_1) se ...

  2. 左侧高亮(js)

    //左导航高亮 (function(){ var leftnav = $('#leftnav');  //左侧ul var pi = __preset.curpathid; if(pi=='/acco ...

  3. IOS 缩放图片常用方法

    /** * 指定Size压缩图片 (图片会压缩变形) * * @param image 原图 * @param size 压缩size * * @return 压缩后的图片 */ -(UIImage* ...

  4. iOS 消息推送原理

    一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图: 1. Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Prov ...

  5. App上线流程全攻略(续)-iOS8之后的改动与所遇日常错误

    随着iOS8的公布,iTunes Connect的界面也是发生了非常大的改变,App 上传到 Store上面的步骤也是发生了些改变.以下继续用图说话: /*********************** ...

  6. hdu4126Genghis Khan the ConquerorGenghis Khan the Conqueror(MST+树形DP)

    题目请戳这里 题目大意:给n个点,m条边,每条边权值c,现在要使这n个点连通.现在已知某条边要发生突变,再给q个三元组,每个三元组(a,b,c),(a,b)表示图中可能发生突变的边,该边一定是图中的边 ...

  7. Swift 2.0初探:值得注意的新特性

    转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心.我同样也是非常喜爱这门新的编程语言. 今年6月,一年一度的WWDC大会如期而至,在大会上A ...

  8. CentOS7 安装LNMP(Linux+Nginx+MySQL+PHP)

    由于工作须要,须要学习php,本来想安装lamp的可是考虑到如今nginxserver有良好的性能且应用广泛. 这里我决定搭建Linux(CentOS7+Nginx+MySQL+PHP)下的webse ...

  9. html的特质语义:微格式及其他(重点介绍其中两种)

    今天再次翻开html的书本, 感觉过了个周末似乎生疏了许多, 虽然我是刚接触html的, 但是对于他还是抱有极其大的兴趣的, 所以不爱看书的我, 也开始一遍遍的翻阅着书本, 寻找解决问题的方法, 下面 ...

  10. MySQL Replication Error 处理一例

    故障现象 MySQL slave status详情 mysql> show slave status\G *************************** 1. row ********* ...