• SegmentControl 那些令人烦恼的事儿


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

    UISegmentControl

    • 概述

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

        注: 上边的是 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];
      ```

        - 效果
        
        	![](http://images2015.cnblogs.com/blog/783575/201603/783575-20160324170133448-1759858690.gif)
      
      
        	
        		注:此时我们会看到,当选中 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 还可以,那么请点赞喽。非常感谢您的支持

  • 相关阅读:
    spark streaming 整合kafka(二)
    JAVA设计模式之动态代理
    使用org.apache.commons.cli包来设计JAVA命令行工具
    HTML教程
    Java InputStream和Reader
    Java IO
    程序员怎么把自己的招牌打出去?
    Java设计模式之单例模式
    JAVA NIO
    Java文件流字节流和字符流的区别
  • 原文地址:https://www.cnblogs.com/theDesertIslandOutOfTheWorld/p/5316288.html
Copyright © 2020-2023  润新知