自定义Switch控件

简介: 做iOS开发也有一段时间的了,项目中的开关控件一直都是用的系统级别的,至多就是给UISwitch控件换一个tintColor。这次的UI设计师设计了一个带有动画效果的UISwitch控件。

前言

做iOS开发也有一段时间的了,项目中的开关控件一直都是用的系统级别的,至多就是给UISwitch控件换一个tintColor。这次的UI设计师设计了一个带有动画效果的UISwitch控件。如下图:


一开始为了快速开发就没在在意这个小问题,用的系统的。但是子啊UI做评审的时候说我的这个还原度是不OK的。那怎么办呢,想办法自己写一个吧.....(其实我们也想过用Airbnb的那个开原动画框架的,但是....种种原因吧,没用)。

想法

基本的思路就是现在底部放上一个UIView做底,然后上面做一个UIbutton,实现开关的效果,然后再点击开关的时候出发一下动画效果,从而达到UI的还原度要求。

撸起袖子,就是干 ! ! !

首先我们在.h文件中写下如下的代码:


@protocol  SwitchViewDelegate <NSObject>

- (void)switchView_didChangeValue:(SwitchView *)zpswitch value:(BOOL)value;

@end

@interface SwitchView : UIView

@property (nonatomic, assign) BOOL on;

@property (nonatomic, weak) id<SwitchViewDelegate> delegate;

@end

最上面的SwitchViewDelegate的方法是在我们点击按钮切换当前的选中状态的。on这个属性暴出去的目的方便使用者去设置一开始的初始状态和之后的状态切换。

然后我们去点击.m文件。
我们要在这里声明两个属性:


@interface SwitchView()

@property (nonatomic,strong) UIButton *switchButton;

@property (nonatomic,assign) BOOL isFirst;

@end

switchButton这是用来点击的UIButton控件,就和我们平时创建,初始化没什么区别。
isFirst这个是一个标志位,用来判断是不是第一次显示的问题。
我们把相关的属性声明都弄完了之后就开始正题。

初始化

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.isFirst = YES;
        self.backgroundColor = [UIColor colorWithRed:242/255.0 green:242/255.0 blue:242/255.0 alpha:1];
        self.layer.masksToBounds = YES;
        self.layer.borderColor = [UIColor colorWithRed:218/255.0 green:218/255.0 blue:218/255.0 alpha:1].CGColor;
        self.layer.borderWidth = 1.0f;
        self.layer.cornerRadius = self.bounds.size.height / 2.0;
        self.clipsToBounds = YES;
        [self addSubview:self.switchButton];
        self.switchButton.layer.cornerRadius = self.switchButton.bounds.size.height / 2.0;
    }
    return self;
}
- (UIButton *)switchButton{
    if (!_switchButton) {
        _switchButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _switchButton.frame = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.height);
        [_switchButton setImage:[UIImage imageNamed:@"Group 8"] forState:UIControlStateNormal];
        [_switchButton setImage:[UIImage imageNamed:@"Group 2"] forState:UIControlStateSelected];
        [_switchButton addTarget:self action:@selector(switcherButtonTouch:) forControlEvents:UIControlEventTouchUpInside];
    }
    return _switchButton;
}

这里就是懒加载了一个UIButton,还有就是设置有了一些圆角的属性。这么设置圆角的属性并不是最佳的办法,但是就是一个Demo示例。所以并没有计较那么多。

交互

在上面你们看到了,我们给button添加了一个点击方法,这里我们就要去实现这个方法

- (void)switcherButtonTouch:(UIButton *)sender{
    self.on = !self.on;
    if (self.delegate && [self.delegate respondsToSelector:@selector(switchView_didChangeValue:value:)]) {
        [self.delegate switchView_didChangeValue:self value:self.on];
    }
}

这里就是在变化button的值得时候,通过delegate方法传到控制器或者相应的superView

动画

- (void)animationSwitcherButton{
    if (self.on) {
        [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
            rotateAnimation.fromValue = [NSNumber numberWithFloat:-M_PI];
            rotateAnimation.toValue = [NSNumber numberWithFloat:0.0];
            rotateAnimation.duration = 0.45;
            rotateAnimation.cumulative = NO;
            [self.switchButton.layer addAnimation:rotateAnimation forKey:@"rotate"];

            self.backgroundColor = [UIColor colorWithRed:0/255.0 green:141/255.0 blue:150/255.0 alpha:1];
            self.layer.masksToBounds = YES;
            self.layer.borderColor = [UIColor colorWithRed:0/255.0 green:141/255.0 blue:150/255.0 alpha:1].CGColor;
            self.layer.borderWidth = 1.0f;

            self.switchButton.selected = YES;
            self.switchButton.frame = CGRectMake(self.bounds.size.width - self.bounds.size.height, 0, self.bounds.size.height, self.bounds.size.height);
        } completion:^(BOOL finished) {
            self.switchButton.selected = YES;
            self.switchButton.frame = CGRectMake(self.bounds.size.width - self.bounds.size.height, 0, self.bounds.size.height, self.bounds.size.height);
        }];
    }else{
        [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
            rotateAnimation.toValue = [NSNumber numberWithFloat:-M_PI];
            rotateAnimation.fromValue = [NSNumber numberWithFloat:0.0];
            rotateAnimation.duration = 0.45;
            rotateAnimation.cumulative = NO;
            [self.switchButton.layer addAnimation:rotateAnimation forKey:@"rotate"];
            self.backgroundColor = [UIColor colorWithRed:242/255.0 green:242/255.0 blue:242/255.0 alpha:1];
            self.layer.masksToBounds = YES;
            self.layer.borderColor = [UIColor colorWithRed:218/255.0 green:218/255.0 blue:218/255.0 alpha:1].CGColor;
            self.layer.borderWidth = 1.0f;
            self.switchButton.selected = NO;
            self.switchButton.frame = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.height);
        } completion:^(BOOL finished) {
            self.switchButton.selected = NO;
           self.switchButton.frame = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.height);
        }];
    }
}

这里面做了一个旋转的动画和平移的动画。到现在为止你就可以实现基本的要求了。当你从不被选中切换到选中的时候,感觉棒棒哒。但是这个交互只能在点击button的时候会有,和系统的那个UISwitch还是有着一定的区别的。还有就是如果一开始就是选中的状态,现在的代码还不能完全满足。
so

优化一下

1.增加一个手势,让点击这个控件的时候就可以响应变化方法:
我们在上面的那个- (instancetype)initWithFrame:(CGRect)frame方法中添加一个tap手势。

 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(switcherButtonTouch:)];
[self addGestureRecognizer:tap];

2.让使用者自由的设置on的初始值:

- (void)setOn:(BOOL)on{
    _on = on;
    if (_on && self.isFirst) {
        self.backgroundColor = [UIColor colorWithRed:0/255.0 green:141/255.0 blue:150/255.0 alpha:1];
        self.layer.masksToBounds = YES;
        self.layer.borderColor = [UIColor colorWithRed:0/255.0 green:141/255.0 blue:150/255.0 alpha:1].CGColor;
        self.layer.borderWidth = 1.0f;
        self.switchButton.selected = YES;
        self.switchButton.frame = CGRectMake(self.bounds.size.width - self.bounds.size.height, 0, self.bounds.size.height, self.bounds.size.height);
        self.isFirst = NO;
    }else{
        [self animationSwitcherButton];
        self.isFirst = NO;
    }
}

通过on的值和self.isFirst的联合判断,从而达到优化效果

实现

    SwitchView *switchView1 = [[SwitchView alloc]initWithFrame:CGRectMake(100, 200, 46, 32)];
    switchView1.on = NO;
    switchView1.delegate = self;
    [self.view addSubview:switchView1];

效果

传送门

https://github.com/cAibDe/SwitchView

相关文章
|
6月前
|
JavaScript 前端开发
Bootstrap-Switch开关控件使用指南
Bootstrap-Switch开关控件使用指南
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
|
4月前
Element UI【实战范例】下拉选择 el-select 的 change 事件传入选中值+自定义参数
Element UI【实战范例】下拉选择 el-select 的 change 事件传入选中值+自定义参数
736 1
|
前端开发
switch对按钮进行判断操作
switch对按钮进行判断操作
79 0
|
Java Android开发
Spinner(列表选项框)的基本使用
这一节是想给大家介绍一个Gallery(画廊)的一个控件,尽管我们可以不通过兼容使用Gallery,不过想想还是算了,因为Gallery在每次切换图片的时候,都需要重新创建视图,这样无疑会造成很大的资源浪费!我们可以通过其他方法来实现Gallery效果,比如通过HorizontalScrollView 来实现水平滚动效果,或者编写一个水平方向的ListView~有兴趣自己谷歌!
131 0
|
JavaScript 开发工具 git
Element-ui中 表格(Table)组件中 toggleRowSelection 方法设置默认多选项 无法选中解决思路
Element-ui中 表格(Table)组件中 toggleRowSelection 方法设置默认多选项 无法选中解决思路
1285 0
Element-ui中 表格(Table)组件中 toggleRowSelection 方法设置默认多选项 无法选中解决思路
|
API Windows 容器
MFC应用程序——标签控件_IP控件_时间控件_List Control控件_Tree Control控件_命令按钮_列表框_组合框_图片_滚动控件(上)
MFC应用程序——标签控件_IP控件_时间控件_List Control控件_Tree Control控件_命令按钮_列表框_组合框_图片_滚动控件
205 0
MFC应用程序——标签控件_IP控件_时间控件_List Control控件_Tree Control控件_命令按钮_列表框_组合框_图片_滚动控件(上)
写一个原生switch开关按钮
欢迎阅读本博文,本博文主要讲述【写一个原生switch开关按钮】,文字通俗易懂,如有不妥,还请多多指正。
写一个原生switch开关按钮
MFC应用程序——标签控件_IP控件_时间控件_List Control控件_Tree Control控件_命令按钮_列表框_组合框_图片_滚动控件(下)
MFC应用程序——标签控件_IP控件_时间控件_List Control控件_Tree Control控件_命令按钮_列表框_组合框_图片_滚动控件
169 0