自定義Switch控制元件

c4ibD3發表於2019-01-02

前言

做iOS開發也有一段時間的了,專案中的開關控制元件一直都是用的系統級別的,至多就是給UISwitch控制元件換一個tintColor。這次的UI設計師設計了一個帶有動畫效果的UISwitch控制元件。如下圖:

自定義Switch控制元件
自定義Switch控制元件

一開始為了快速開發就沒在在意這個小問題,用的系統的。但是子啊UI做評審的時候說我的這個還原度是不OK的。那怎麼辦呢,想辦法自己寫一個吧.....(其實我們也想過用Airbnb的那個開原動畫框架的,但是....種種原因吧,沒用)。

想法

基本的思路就是現在底部放上一個UIView做底,然後上面做一個UIbutton,實現開關的效果,然後再點選開關的時候出發一下動畫效果,從而達到UI的還原度要求。

自定義Switch控制元件

擼起袖子,就是幹 ! ! !

首先我們在.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];
複製程式碼

效果

自定義Switch控制元件

傳送門

github.com/cAibDe/Swit…

相關文章