SegmentControl 那些令人烦恼的事儿
每个人的曾经都很苦逼。我知道我很卑微,但我不曾放慢脚步,在这条路上至死不悔。愿与你同行。
UISegmentControl
概述
- UISegmentControl 是系统的段选择控件,具有简洁大方的外观,但是通常不能满足产品设计的需求。用户( developer )对 UISegmentControl 的外观的可控性是比较差的,为了满足我们
完美的产品设计需求,我们通常需要绞尽脑汁的思考如何去改变 UISegmentControl 的外观,但结果却不那么令人满意。最终你会发现 UISegmentControl 满足需求需要花费很多的时间(或者说初学者根本无法完成需求)。在此,首先简单叙述 UISegmentControl 的实现原理,和事件处理;然后,详细介绍如何自定义 IDSegmentControl,并与 UISegmentControl 对比。 大家在能使用系统控件的情况下,尽量使用系统控件。本 Blog 自定义 IDSegmentControl 只是向大家提供一种解决问题的方式。
- UISegmentControl 是系统的段选择控件,具有简洁大方的外观,但是通常不能满足产品设计的需求。用户( developer )对 UISegmentControl 的外观的可控性是比较差的,为了满足我们
效果图

注: 上边的是 UISegmentControl 实现的效果,下边是 IDSegmentControl 实现的效果
UISegmentControl(下面以 UISegmentControl 的示例,来叙述其使用细节)
初始化 UISegmentControl 实例,并设置标题(默认状态下,UISegmentControl不选中任何一个 segment)
self.segmentControl = [[UISegmentedControl alloc] initWithItems:@[@"全部分类", @"智能排序"]];
效果

设置标题的字体和颜色(选中第一个 segment,此时的背景色为蓝色)
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
dictionary[NSForegroundColorAttributeName] = [UIColor blackColor];
dictionary[NSFontAttributeName] = [UIFont systemFontOfSize:18];
[self.segmentControl setTitleTextAttributes:dictionary forState:UIControlStateNormal];
效果

设置 segment 三个状态(不要问那三个状态哦)下的图片
[self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[self.segmentControl setBackgroundImage:[UIImage imageNamed:@"selected"] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
[self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
```- 效果  注:此时我们会看到,当选中 segment 时,文字会被遮挡(你可以让美工给你切个图哦,如果美工说:“你长的太丑了,不给切”。那你可以联系我,我会告诉你怎么做。`代码能解决的问题,就不用去找美工喽`)。
改变分割线
使用 tintColor
[self.segmentControl setTintColor:[UIColor whiteColor]];
效果

使用 dividerImage(设置一张图片即可)
- (void)setDividerImage:(nullable UIImage *)dividerImage forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState barMetrics:(UIBarMetrics)barMetrics
默认选中第一个 segment
self.segmentControl.selectedSegmentIndex = 0;
效果

为 UISegmentControl 添加事件
[self.segmentControl addTarget:self action:@selector(segmentControlselectedSegmentIndexChange:) forControlEvents:UIControlEventValueChanged];
至此,UISegmentControl 的使用已经简单介绍到此。接下来,会介绍如何实现自己的 segmentControl,如:IDSegmentControl。若你觉得没有必要,那就可以不要往下看了,你不会损失很多。但还是建议你看一下,因为我相信你会收获很多。
IDSegmentControl
概述
- 之所以自定义 IDSegmentControl,是为了增加其可控性,使其用起来更得心应手。同时也是一种技术的积淀。
设计思路
使用 UIView 的子类来实现 IDSegmentControl,每个 Item 是一个 Button,通过控制 Button 来控制 segment 的外观和事件
使用代理模式来实现 IDSegmentControl 的事件处理(
继承自 UIControl 的版本,会使用 addTarget 实现)注:
或许你的自定控件,通常是继承自 UIView,但是你是否想过让它继承自 UIControl 呢!!!若你有兴趣,那么请联系我,感谢您的支持
具体实现
设计接口
设置 IDSegmentControl 的 items 的 title(
IDSegmentControl 中 item 和 indicator 的布局需要基于 items 的 count)/** 所有segment的标题 */
@property (nonatomic, strong) NSArray *titlesOfSegments;
#pragma mark - overided setter, update the appreance of the segmentControl
- (void)setTitlesOfSegments:(NSArray *)titlesOfSegments {
_titlesOfSegments = titlesOfSegments;
// 根据 _titlesOfSegments 向 IDSegmentControl 中添加 items(button)
for (NSInteger i = 0; i < _titlesOfSegments.count; i++) {
UIButton *segmentButton = [[UIButton alloc] init];
[segmentButton.titleLabel setFont:[UIFont systemFontOfSize:18]];
[segmentButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[segmentButton setTitle:_titlesOfSegments[i] forState:UIControlStateNormal];
[segmentButton addTarget:self action:@selector(segmentButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.segmentArray addObject:segmentButton];
[self addSubview:segmentButton];
}
}
搭建 IDSegmentControl 的界面
添加分割线和指示器
#pragma mark - initializer
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.seperatorView];
[self addSubview:self.indicatorView];
}
return self;
}
布局所有的子控件(为什么在 layoutSubviews 布局子控件,原因想必大家都知道的)
/** 布局所有的子控件 */
- (void)layoutSubviews {
[super layoutSubviews];
// 底部的分割线
self.seperatorView.frame = CGRectMake(0, self.bounds.size.height - SeperatorHeight, self.bounds.size.width, SeperatorHeight);
// 指示器
self.indicatorView.frame = CGRectMake(0, self.bounds.size.height - SeperatorHeight, self.bounds.size.width / (CGFloat)self.titlesOfSegments.count, SeperatorHeight);
// 所有的 segment
CGFloat segmentWith = self.bounds.size.width / (CGFloat)self.titlesOfSegments.count;
CGFloat segmentHeight = self.bounds.size.height;
for (NSInteger i = 0; i < self.segmentArray.count; i++) {
self.segmentArray[i].frame = CGRectMake(i * segmentWith, 0, segmentWith, segmentHeight - 1);
}
}
为 IDSegmentControl 添加协议
@protocol IDSegementControlDelegate <NSObject>
@optional
- (void)segmentControlDidSelectItem:(UIButton *)selectedItem atIndex:(NSInteger)selectedIndex;
@end
为 IDSegmentControl 添加接口
/** 代理 */
@property (nonatomic, weak) id<ZBSegementControlDelegate> delegate;
/** 选中的segment的索引 */
@property (nonatomic, assign) NSInteger selectedIndex;
/** 指示器的偏移量 */
@property (nonatomic, assign) CGFloat indicatorOffsetX;
处理按钮事件
#pragma mark - 按钮点击事件
- (void)segmentButtonClick:(UIButton *)segmentButton {
// 处理选中 segment 的逻辑
if (![self.lastSegmentButton isEqual:segmentButton]) {
self.lastSegmentButton.selected = NO;
}
segmentButton.selected = !segmentButton.selected;
// 改变 indicator 的位置
CGFloat segmentWidth = self.bounds.size.width / (CGFloat)self.titlesOfSegments.count;
// 同样是 segmentButton,你知道为什么可以找到对应的 segmentButton 吗?
NSInteger selectedIndex = [self.segmentArray indexOfObject:segmentButton];
[UIView animateWithDuration:0.25 animations:^{
[self setIndicatorOffsetX:selectedIndex * segmentWidth];
}];
// 通知代理,选中的 segment 已经改变
if ([self.delegate respondsToSelector:@selector(segmentControlDidSelectItem:atIndex:)]) {
[self.delegate segmentControlDidSelectItem:segmentButton atIndex:selectedIndex];
}
// 更新上一次选中的 segment(当前的 segment 是下一次选中新的 segment 时的 lastSegmentButton。好好理解吧)
if (self.lastSegmentButton == nil) {
self.lastSegmentButton = segmentButton;
} else {
if (![self.lastSegmentButton isEqual:segmentButton]) {
self.lastSegmentButton = segmentButton;
}
}
}
viewController 使用示例
添加 segmentControl
/** UISegmentedControl */
- (void)setupSystemSegmentControl {
// titles
self.segmentControl = [[UISegmentedControl alloc] initWithItems:@[@"全部分类", @"智能排序"]];
// titleAttributes
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
dictionary[NSForegroundColorAttributeName] = [UIColor blackColor];
dictionary[NSFontAttributeName] = [UIFont systemFontOfSize:18];
[self.segmentControl setTitleTextAttributes:dictionary forState:UIControlStateNormal];
// backgroundImage
[self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[self.segmentControl setBackgroundImage:[UIImage imageNamed:@"selected"] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
// tintColor
[self.segmentControl setTintColor:[UIColor whiteColor]];
// 选中第一个 segment
self.segmentControl.selectedSegmentIndex = 0;
// action
[self.segmentControl addTarget:self action:@selector(systemSegmentControlselectedSegmentIndexChange:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:self.segmentControl];
}
/** IDSegmentControl */
- (void)setupIDSegmentControl {
self.customSegmentControl = [[IDSegmentControl alloc] init];
self.customSegmentControl.delegate = self;
self.customSegmentControl.titlesOfSegments = @[@"全部分类", @"智能排序"];
[self.view addSubview:self.customSegmentControl];
}
布局子控件
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.segmentControl.frame = CGRectMake(0, 64, self.view.bounds.size.width, 35);
self.customSegmentControl.frame = CGRectMake(0, 104, self.view.bounds.size.width, 35);
}
处理 segmentControl 事件
// UISegmentControl
- (void)systemSegmentControlselectedSegmentIndexChange:(UISegmentedControl *)segmentControl {
NSLog(@"%zd", segmentControl.selectedSegmentIndex);
}
// IDSegmentControl
- (void)segmentControlDidSelectItem:(UIButton *)selectedItem atIndex:(NSInteger)selectedIndex {
NSLog(@"%zd", selectedIndex);
}
声明:若你需要工程文件,请@我喽。若您觉得 Blog 还可以,那么请点赞喽。非常感谢您的支持
SegmentControl 那些令人烦恼的事儿的更多相关文章
- ORACLE-023:令人烦恼的 ora-01722 无效数字
https://blog.csdn.net/yysyangyangyangshan/article/details/51762746
- Java如何解决脆弱基类(基类被冻结)问题
概述 大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系).实际上80%的代码应该完全用interfaces写,而不是通过extends.“JAVA设计模式”一书详细阐述了怎样用 ...
- Qt实用小技巧(转)
原博网址:http://www.cnblogs.com/feiyangqingyun/archive/2010/12/06/1898143.html 1.如果在窗体关闭前自行判断是否可关闭答:重新实现 ...
- Win7系统安装好Axure点击运行报.NET Framework4.0未安装的解决办法
1:问题 由于工作需要,需要研究一下Axure原型设计软件的使用方式,在公司的电脑上成功安装了从同事那里拿来的Axure7.0软件,能够正确运行没有任何问题,在自己的电脑上安装的也非常顺利,不过运 ...
- 委托、IOC全知道
话说写代码已有数年,曾经花了很多时间,看了很多大牛的文章也是不能参透,日思夜想都没有理解的概念,通过不断的实践与学习,回过头来再看,总算有了一个清晰的理解与认识,也看到一句话说,最好的学习就是把别人教 ...
- html5 Web Workers
虽然在JavaScript中有setInterval和setTimeout函数使javaScript看起来好像使多线程执行,单实际上JavaScript使单线程的,一次只能做一件事情(关于JavaSc ...
- 安装 Ubuntu 后的个人常用配置
在 ASA 猪队友的带领下,拥抱开源世界,用上了Ubuntu.资深强迫症现身说法,配置符合自己使用习惯的Ubuntu. 1. 窗口标题栏显示菜单项 打开系统设置->外观->行为,在[显示窗 ...
- 从 MySQL+MMM 到 MariaDB+Galera Cluster : 一个高可用性系统改造
很少有事情比推出高可用性(HA)系统之后便经常看到的系统崩溃更糟糕.对于我们这个Rails运行机的团队来说,这个失效的HA系统是MySQL多主复制管理器(MMM). 我们已经找寻MMM的替代品有一段时 ...
- Linux GDB Debugging
Catalog . GDB Introduction . GDB基本命令 1. GDB Introduction GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师 ...
随机推荐
- ASP.NET 5 Beta 7 版本
在 VS2015 发布的同时,微软也发布了 ASP.NET 5 的路线图(详见ASP.NET 5 Schedule and Roadmap : https://github.com/aspnet/ho ...
- MySQL有趣的查询方式
背景介绍 美国大选开始了,国防部要求我对两个总统候选人的票数进行统计.我首先简单的进行一次无条件查询,了解了一下表格的结构及所有数据长什么样子. select * from foo 查询到的结果令我很 ...
- C# 深拷贝的具体代码的封装与调用
先封装下实现方法: public class DeepClone { public static object CopyObject(Object obj) { if (obj == null) { ...
- 7. SVM松弛变量
我们之前讨论的情况都是建立在样例线性可分的假设上,当样例线性不可分时,我们可以尝试使用核函数来将特征映射到高维,这样很可能就可分了.然而,映射后我们也不能100%保证可分.那怎么办呢,我们需要将模型进 ...
- C# 合并及拆分PDF文件
C# 合并及拆分PDF文件 有时我们可能会遇到下图这样一种情况 — 我们需要的资料或教程被分成了几部分存放在多个PDF文件中,不管是阅读还是保存都不是很方便,这时我们肯定想要把这些PDF文件合并为一个 ...
- JVM学习(2)——技术文章里常说的堆,栈,堆栈到底是什么,从os的角度总结
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 堆栈是栈 JVM栈和本地方法栈划分 Java中的堆,栈和c/c++中的堆,栈 数据结构层面的堆,栈 os层面 ...
- php内核分析(五)-zval
这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux 实际上,从这个函数开始,就已经进入到了zend引擎的范围了. zend_eval_string_ex(exec_direc ...
- jQuery操作DOM元素
作为一个后端程序员,也是要和前端页面打交道的.最常见的场景莫过DOM元素操作和前端页面使用AJAX向服务器发送请求.实现上述两个功能当然可以使用原生js来完成,但在实际开发过程中很少这样做,通常会使用 ...
- sql server查询可编程对象定义的方式对比以及整合
本文目录列表: 1.sql server查看可编程对象定义的方式对比 2.整合实现所有可编程对象定义的查看功能的存储dbo.usp_helptext2 3.dbo.helptext2的选择性测试 4. ...
- Guass列选主元消去法和三角分解法
最近数值计算学了Guass列主消元法和三角分解法解线性方程组,具体原理如下: 1.Guass列选主元消去法对于AX =B 1).消元过程:将(A|B)进行变换为,其中是上三角矩阵.即: k从1到n-1 ...