OC

利用视错觉创建自定义控件

Posted by sunzhongliang on April 21, 2020

一个利用视错觉的自定义组件

第一次看到这个组件效果时,感觉没有什么特别之处,就是一个普通的再也普通不过的组件。可是再仔细看就感觉不一样了,一些细节处理的非常好,先来看一下运行效果吧。 看上去,感觉就是几个Button, 然后红色的是一个UIView, 点击那个Button时,就把UIView通过动画的形式移动到当前点击的Button。
但随后仔细一想,感觉还是太年轻了,这个组件实现的重点与难点不在于如何去运动,如何去封装它。 如果把动画放慢,会发现一个细节,这个细节处理的非常的巧妙,也是这个组件的亮点与难点所在。下方是切换时放慢的一个效果。看到这个细节时,瞬间颠覆了我之前单纯的想法。这个组件远远没有我想的这么简单。
在切换时,有一个细节,就是在红色区域中的文字(或文字的一部分)随着红色区域的移动,文字的颜色也会随之部分改变。当红色区域移动过后,字体颜色又变为原来的了, 如下图:

实现原理

难点在于滑块区域中的文字部分的颜色是白色,一开始想到用富文本去处理各个黑色label,但实现起来要计算边界太麻烦了果断放弃;后来想了一下可不可以利用一个view覆盖白色label的方式来实现呢?于是乎思路逐渐明了,简单来说也就是以下几步:

  1. 在页面上创建底层的label(黑色字体)
  2. 在页面上创建一个红色的滑块View,覆盖在黑色字体label上面
  3. 在红色的滑块View上面添加一个透明的View,宽度和黑色字体的总宽度相等,然后设置红色滑块View超出部分不显示
  4. 在透明View上面再添加label(白色字体),注意白色label一定要和黑色字体label大小一模一样
  5. 当翻页移动滑块时,让白色字体的label始终和黑色字体的label对齐

在移动红色滑块时,红色滑块会覆盖最底层的黑色label,这样就遮挡住住红色滑块后面的黑色label不显示;
红色滑块上面有一个透明的view,透明view上面有添加白色字体的label,这样滑块滑动到哪里,白色字体label就会在红色滑块上面显示出来,由于红色滑块设置了超出部分不展示,所以透明view上面的白色label是不会在红色滑块外面显示的,这样也就不会影响到黑色label。

代码实现:

NSArray *array = @[@"家装",@"衣服",@"鞋类",@"数码"];

CGFloat width = 90;
CGFloat height = 35;
for (int i = 0; i < array.count; i++) {
    // 1.创建最底层的label(黑色)
    UILabel *bottomLabel = [[UILabel alloc] init];
    bottomLabel.frame = CGRectMake(i*width, 100, width, height);
    bottomLabel.textColor = [UIColor blackColor];
    bottomLabel.textAlignment = NSTextAlignmentCenter;
    bottomLabel.text = array[i];
    [self.view addSubview:bottomLabel];
}

// 2.添加一个红色的view
UIView *redView = [[UIView alloc] init];
redView.frame = CGRectMake(0, 100, width, height);
redView.backgroundColor = [UIColor redColor];
redView.clipsToBounds = YES; // 超出部分不显示
redView.layer.cornerRadius = 15;
[self.view addSubview:redView];
_redView = redView;


// 3.在红色的view上面再添加一个容器,用于放白色字体label
UIView *whiteView = [[UIView alloc] init];
whiteView.frame = CGRectMake(0, 0, width*array.count, height);
[_redView addSubview:whiteView];
_whiteView = whiteView;

// 3.添加白色的label
for (int i = 0; i < array.count; i++) {
    UILabel *bottomLabel = [[UILabel alloc] init];
    bottomLabel.frame = CGRectMake(i*width, 0, width, height);
    bottomLabel.textColor = [UIColor whiteColor];
    bottomLabel.textAlignment = NSTextAlignmentCenter;
    bottomLabel.text = array[i];
    [whiteView addSubview:bottomLabel];
}

// 点击背景就让滑块移动用于测试效果
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 红色滑块向右移动
    CGRect frame = _redView.frame;
    frame.origin.x += 10;
    _redView.frame = frame;
    
    // 透明view向左移动,这样透明view就始终和黑色label坐标一致
    CGRect oframe = _whiteView.frame;
    oframe.origin.x -= 10;
    _whiteView.frame = oframe;
}

本文首次发布于 孙忠良 Blog, 作者 [@sunzhongliang] , 转载请保留原文链接.