每个人的曾经都很苦逼。我知道我很卑微,但我不曾放慢脚步,在这条路上至死不悔。愿与你同行。

UISegmentControl

  • 概述

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

    1. 注: 上边的是 UISegmentControl 实现的效果,下边是 IDSegmentControl 实现的效果
  • UISegmentControl(下面以 UISegmentControl 的示例,来叙述其使用细节)

    • 初始化 UISegmentControl 实例,并设置标题(默认状态下,UISegmentControl不选中任何一个 segment)

      1. self.segmentControl = [[UISegmentedControl alloc] initWithItems:@[@"全部分类", @"智能排序"]];
      • 效果

    • 设置标题的字体和颜色(选中第一个 segment,此时的背景色为蓝色)

      1. NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
      2. dictionary[NSForegroundColorAttributeName] = [UIColor blackColor];
      3. dictionary[NSFontAttributeName] = [UIFont systemFontOfSize:18];
      4. [self.segmentControl setTitleTextAttributes:dictionary forState:UIControlStateNormal];
      • 效果

    • 设置 segment 三个状态(不要问那三个状态哦)下的图片

      1. [self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
      2. [self.segmentControl setBackgroundImage:[UIImage imageNamed:@"selected"] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

    [self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];

    ```

    1. - 效果
    2. ![](http://images2015.cnblogs.com/blog/783575/201603/783575-20160324170133448-1759858690.gif)
    3. 注:此时我们会看到,当选中 segment 时,文字会被遮挡(你可以让美工给你切个图哦,如果美工说:“你长的太丑了,不给切”。那你可以联系我,我会告诉你怎么做。`代码能解决的问题,就不用去找美工喽`)。
    • 改变分割线

      • 使用 tintColor

        1. [self.segmentControl setTintColor:[UIColor whiteColor]];
        • 效果

      • 使用 dividerImage(设置一张图片即可)

        1. - (void)setDividerImage:(nullable UIImage *)dividerImage forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState barMetrics:(UIBarMetrics)barMetrics
    • 默认选中第一个 segment

      1. self.segmentControl.selectedSegmentIndex = 0;
      • 效果

    • 为 UISegmentControl 添加事件

      1. [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

        1. /** 所有segment的标题 */
        2. @property (nonatomic, strong) NSArray *titlesOfSegments;
        3. #pragma mark - overided setter, update the appreance of the segmentControl
        4. - (void)setTitlesOfSegments:(NSArray *)titlesOfSegments {
        5. _titlesOfSegments = titlesOfSegments;
        6. // 根据 _titlesOfSegments 向 IDSegmentControl 中添加 items(button)
        7. for (NSInteger i = 0; i < _titlesOfSegments.count; i++) {
        8. UIButton *segmentButton = [[UIButton alloc] init];
        9. [segmentButton.titleLabel setFont:[UIFont systemFontOfSize:18]];
        10. [segmentButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        11. [segmentButton setTitle:_titlesOfSegments[i] forState:UIControlStateNormal];
        12. [segmentButton addTarget:self action:@selector(segmentButtonClick:) forControlEvents:UIControlEventTouchUpInside];
        13. [self.segmentArray addObject:segmentButton];
        14. [self addSubview:segmentButton];
        15. }
        16. }
    • 搭建 IDSegmentControl 的界面

      • 添加分割线和指示器

        1. #pragma mark - initializer
        2. - (instancetype)initWithFrame:(CGRect)frame {
        3. if (self = [super initWithFrame:frame]) {
        4. [self addSubview:self.seperatorView];
        5. [self addSubview:self.indicatorView];
        6. }
        7. return self;
        8. }
      • 布局所有的子控件(为什么在 layoutSubviews 布局子控件,原因想必大家都知道的)

        1. /** 布局所有的子控件 */
        2. - (void)layoutSubviews {
        3. [super layoutSubviews];
        4. // 底部的分割线
        5. self.seperatorView.frame = CGRectMake(0, self.bounds.size.height - SeperatorHeight, self.bounds.size.width, SeperatorHeight);
        6. // 指示器
        7. self.indicatorView.frame = CGRectMake(0, self.bounds.size.height - SeperatorHeight, self.bounds.size.width / (CGFloat)self.titlesOfSegments.count, SeperatorHeight);
        8. // 所有的 segment
        9. CGFloat segmentWith = self.bounds.size.width / (CGFloat)self.titlesOfSegments.count;
        10. CGFloat segmentHeight = self.bounds.size.height;
        11. for (NSInteger i = 0; i < self.segmentArray.count; i++) {
        12. self.segmentArray[i].frame = CGRectMake(i * segmentWith, 0, segmentWith, segmentHeight - 1);
        13. }
        14. }
    • 为 IDSegmentControl 添加协议

      1. @protocol IDSegementControlDelegate <NSObject>
      2. @optional
      3. - (void)segmentControlDidSelectItem:(UIButton *)selectedItem atIndex:(NSInteger)selectedIndex;
      4. @end
    • 为 IDSegmentControl 添加接口

      1. /** 代理 */
      2. @property (nonatomic, weak) id<ZBSegementControlDelegate> delegate;
      3. /** 选中的segment的索引 */
      4. @property (nonatomic, assign) NSInteger selectedIndex;
      5. /** 指示器的偏移量 */
      6. @property (nonatomic, assign) CGFloat indicatorOffsetX;
    • 处理按钮事件

      1. #pragma mark - 按钮点击事件
      2. - (void)segmentButtonClick:(UIButton *)segmentButton {
      3. // 处理选中 segment 的逻辑
      4. if (![self.lastSegmentButton isEqual:segmentButton]) {
      5. self.lastSegmentButton.selected = NO;
      6. }
      7. segmentButton.selected = !segmentButton.selected;
      8. // 改变 indicator 的位置
      9. CGFloat segmentWidth = self.bounds.size.width / (CGFloat)self.titlesOfSegments.count;
      10. // 同样是 segmentButton,你知道为什么可以找到对应的 segmentButton 吗?
      11. NSInteger selectedIndex = [self.segmentArray indexOfObject:segmentButton];
      12. [UIView animateWithDuration:0.25 animations:^{
      13. [self setIndicatorOffsetX:selectedIndex * segmentWidth];
      14. }];
      15. // 通知代理,选中的 segment 已经改变
      16. if ([self.delegate respondsToSelector:@selector(segmentControlDidSelectItem:atIndex:)]) {
      17. [self.delegate segmentControlDidSelectItem:segmentButton atIndex:selectedIndex];
      18. }
      19. // 更新上一次选中的 segment(当前的 segment 是下一次选中新的 segment 时的 lastSegmentButton。好好理解吧)
      20. if (self.lastSegmentButton == nil) {
      21. self.lastSegmentButton = segmentButton;
      22. } else {
      23. if (![self.lastSegmentButton isEqual:segmentButton]) {
      24. self.lastSegmentButton = segmentButton;
      25. }
      26. }
      27. }
    • viewController 使用示例

      • 添加 segmentControl

        1. /** UISegmentedControl */
        2. - (void)setupSystemSegmentControl {
        3. // titles
        4. self.segmentControl = [[UISegmentedControl alloc] initWithItems:@[@"全部分类", @"智能排序"]];
        5. // titleAttributes
        6. NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
        7. dictionary[NSForegroundColorAttributeName] = [UIColor blackColor];
        8. dictionary[NSFontAttributeName] = [UIFont systemFontOfSize:18];
        9. [self.segmentControl setTitleTextAttributes:dictionary forState:UIControlStateNormal];
        10. // backgroundImage
        11. [self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
        12. [self.segmentControl setBackgroundImage:[UIImage imageNamed:@"unselected"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        13. [self.segmentControl setBackgroundImage:[UIImage imageNamed:@"selected"] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
        14. // tintColor
        15. [self.segmentControl setTintColor:[UIColor whiteColor]];
        16. // 选中第一个 segment
        17. self.segmentControl.selectedSegmentIndex = 0;
        18. // action
        19. [self.segmentControl addTarget:self action:@selector(systemSegmentControlselectedSegmentIndexChange:) forControlEvents:UIControlEventValueChanged];
        20. [self.view addSubview:self.segmentControl];
        21. }
        22. /** IDSegmentControl */
        23. - (void)setupIDSegmentControl {
        24. self.customSegmentControl = [[IDSegmentControl alloc] init];
        25. self.customSegmentControl.delegate = self;
        26. self.customSegmentControl.titlesOfSegments = @[@"全部分类", @"智能排序"];
        27. [self.view addSubview:self.customSegmentControl];
        28. }
      • 布局子控件

        1. - (void)viewDidLayoutSubviews {
        2. [super viewDidLayoutSubviews];
        3. self.segmentControl.frame = CGRectMake(0, 64, self.view.bounds.size.width, 35);
        4. self.customSegmentControl.frame = CGRectMake(0, 104, self.view.bounds.size.width, 35);
        5. }
      • 处理 segmentControl 事件

        1. // UISegmentControl
        2. - (void)systemSegmentControlselectedSegmentIndexChange:(UISegmentedControl *)segmentControl {
        3. NSLog(@"%zd", segmentControl.selectedSegmentIndex);
        4. }
        5. // IDSegmentControl
        6. - (void)segmentControlDidSelectItem:(UIButton *)selectedItem atIndex:(NSInteger)selectedIndex {
        7. NSLog(@"%zd", selectedIndex);
        8. }

    声明:若你需要工程文件,请@我喽。若您觉得 Blog 还可以,那么请点赞喽。非常感谢您的支持

SegmentControl 那些令人烦恼的事儿的更多相关文章

  1. ORACLE-023:令人烦恼的 ora-01722 无效数字

    https://blog.csdn.net/yysyangyangyangshan/article/details/51762746

  2. Java如何解决脆弱基类(基类被冻结)问题

    概述  大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系).实际上80%的代码应该完全用interfaces写,而不是通过extends.“JAVA设计模式”一书详细阐述了怎样用 ...

  3. Qt实用小技巧(转)

    原博网址:http://www.cnblogs.com/feiyangqingyun/archive/2010/12/06/1898143.html 1.如果在窗体关闭前自行判断是否可关闭答:重新实现 ...

  4. Win7系统安装好Axure点击运行报.NET Framework4.0未安装的解决办法

      1:问题 由于工作需要,需要研究一下Axure原型设计软件的使用方式,在公司的电脑上成功安装了从同事那里拿来的Axure7.0软件,能够正确运行没有任何问题,在自己的电脑上安装的也非常顺利,不过运 ...

  5. 委托、IOC全知道

    话说写代码已有数年,曾经花了很多时间,看了很多大牛的文章也是不能参透,日思夜想都没有理解的概念,通过不断的实践与学习,回过头来再看,总算有了一个清晰的理解与认识,也看到一句话说,最好的学习就是把别人教 ...

  6. html5 Web Workers

    虽然在JavaScript中有setInterval和setTimeout函数使javaScript看起来好像使多线程执行,单实际上JavaScript使单线程的,一次只能做一件事情(关于JavaSc ...

  7. 安装 Ubuntu 后的个人常用配置

    在 ASA 猪队友的带领下,拥抱开源世界,用上了Ubuntu.资深强迫症现身说法,配置符合自己使用习惯的Ubuntu. 1. 窗口标题栏显示菜单项 打开系统设置->外观->行为,在[显示窗 ...

  8. 从 MySQL+MMM 到 MariaDB+Galera Cluster : 一个高可用性系统改造

    很少有事情比推出高可用性(HA)系统之后便经常看到的系统崩溃更糟糕.对于我们这个Rails运行机的团队来说,这个失效的HA系统是MySQL多主复制管理器(MMM). 我们已经找寻MMM的替代品有一段时 ...

  9. Linux GDB Debugging

    Catalog . GDB Introduction . GDB基本命令 1. GDB Introduction GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师 ...

随机推荐

  1. hibernate学习笔记之二 基本环境搭建

    1.创建一个普通的java项目 2.加入Hibernate所有的jar包 3.建立包名com.bjpowernode.hibernate 4.建立实体类User.java package com.bj ...

  2. Android的编码规范

    一.Android编码规范 1.学会使用string.xml文件 在我看来,当一个文本信息出现的次数大于一次的时候就必须要使用string.xml 比如一个保存按钮 , 不规范写法: <Butt ...

  3. Linux进程管理及while循环

    目录 进程的相关概念 进程查看及管理工具的使用 Linux系统作业控制 调整进程优先级 网络客户端工具 bash之while循环 20.1.进程类型 守护进程 daemon,在系统引导过程中启动的进程 ...

  4. 关于安卓6.0权限申请 PermissionDog

    最近在一家公司实习,项目中需要用到适配安卓6.0以上的系统,我本来是想用其他人已经写好的权限申请框架来实现的,但是发现跟我的需求有点小区别,所以就自己写了一个 这个权限申请的帮助类很小,只有一个jav ...

  5. GitHub实战系列~1.环境部署+创建第一个文件 2015-12-9

    GitHub实战系列汇总:http://www.cnblogs.com/dunitian/p/5038719.html ———————————————————————————————————————— ...

  6. Nginx代理功能与负载均衡详解

    序言 Nginx的代理功能与负载均衡功能是最常被用到的,关于nginx的基本语法常识与配置已在上篇文章中有说明,这篇就开门见山,先描述一些关于代理功能的配置,再说明负载均衡详细. Nginx代理服务的 ...

  7. SFTP 命令列表以备查询

    Available commands: ascii Set transfer mode to ASCII binary Set transfer mode to binary cd path Chan ...

  8. Python标准模块--os

    1.模块简介 os模块主要包含普遍的操作系统相关操作,如果开发者希望自己开发的Python应用能够与平台无关,尤其需要关注os这个模块. 2.模块使用 2.1 os模块 1. os.name,输出字符 ...

  9. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(16)-权限管理系统-漂亮的验证码

    系列目录 我们上一节建了数据库的表,但我发现很多东西还未完善起来,比如验证码,我们先做好验证码吧,验证码我们再熟悉不过了,为了防止恶意的登录,我们必须在登录页面加入验证码,下面我将分享一个验证码,这个 ...

  10. Log4net入门(ASP.NET MVC 5篇)

    在前4篇Log4net入门文章中,我们讲述了log4net的一些简单用法,在这一篇中我们主要讲述如何在ASP.NET MVC 5项目中将日志信息写入SQL Server数据库中. 一.创建最简单的AS ...