转载

快速全局替换UISwitch以及给自定义控件关联ValueChanged事件

快速全局替换UISwitch以及给自定义控件关联ValueChanged事件

IU

问题描述

这个问题是我在用自定义点选按钮替换系统的UISwitch时遇到的。
如图,右边的两个按钮就是自定义点选按钮,分别对应之前UISwitch的开启和关闭状态:

快速全局替换UISwitch以及给自定义控件关联ValueChanged事件

我的目标

全局搜索UISwtich,然后全部替换为自己封装的CQSwitch,最后调整一下frame(因为UISwtich的size是定死的,所以替换后肯定是要调整的)即可。

思路

根据产品需求封装一个点选按钮,让它具备UISwitch的所有属性和方法

分析UISwitch

先看看UISwitch的.h文件:

NS_CLASS_AVAILABLE_IOS(2_0) __TVOS_PROHIBITED @interface UISwitch : UIControl @property(nullable, nonatomic, strong) UIColor *onTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;
@property(null_resettable, nonatomic, strong) UIColor *tintColor NS_AVAILABLE_IOS(6_0);
@property(nullable, nonatomic, strong) UIColor *thumbTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic, strong) UIImage *onImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;
@property(nullable, nonatomic, strong) UIImage *offImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic,getter=isOn) BOOL on;

- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;      // This class enforces a size appropriate for the control, and so the frame size is ignored.
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

- (void)setOn:(BOOL)on animated:(BOOL)animated; // does not send action

@end

属性和方法非常少,关键是用到的更少,项目中用到的就一个属性和一个方法:

@property(nonatomic,getter=isOn) BOOL on;

- (void)setOn:(BOOL)on animated:(BOOL)animated;

这对封装自定义点选按钮非常有利:只需添加一个属性实现一个方法就完事了。

封装

.h文件:

@interface CQSwitch : UIButton

/** 是否是开启状态 */
@property(nonatomic, assign, getter=isOn) BOOL on;

/** 设置按钮的点选状态 */
- (void)setOn:(BOOL)on animated:(BOOL)animated;

@end

与要实现的UISwitch的方法完全一致。

但是现在遇到个问题,使用UISwitch时,我们是给UISwitch的UIControlEventValueChanged事件绑定方法的,但是我们点击按钮时并不会触发这个事件。所以,还需要将按钮的点击事件和UIControlEventValueChanged关联起来,这里需要用到一个方法:

sendActionsForControlEvents:
Calls the action methods associated with the specified events.

翻译:调用与指定事件相关联的方法。

让按钮的点击间接关联valueChanged事件:

// 按钮点击,触发valueChanged事件
- (void)buttonClicked {
    self.on = !self.on;
    [self sendActionsForControlEvents:UIControlEventValueChanged];
}

这样一来,点击按钮实际上也就触发了valueChanged事件。

.m文件:

@implementation CQSwitch

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self setBackgroundImage:[UIImage imageNamed:@"orderchoseH"] forState:UIControlStateNormal];
        [self setBackgroundImage:[UIImage imageNamed:@"orderchose"] forState:UIControlStateSelected];
        self.adjustsImageWhenHighlighted = NO; // 长按不变灰
        self.on = NO; // 默认为NO,与UISwitch保持一致
        [self addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchDown];
    }
    return self;
}

// 按钮点击,触发valueChanged事件
- (void)buttonClicked {
    self.on = !self.on;
    [self sendActionsForControlEvents:UIControlEventValueChanged];
}

/** 赋值开启or关闭状态 */
- (void)setOn:(BOOL)on {
    _on = on;
    _on ? (self.selected = YES) : (self.selected = NO);
}

/** 设置按钮的点选状态 */
- (void)setOn:(BOOL)on animated:(BOOL)animated {
    self.on = on;
}

@end

使用

与UISwitch完全一致:

self.switchButton = [[CQSwitch alloc] initWithFrame:CGRectMake(120, 90, 90, 90)];
[self.view addSubview:self.switchButton];
self.switchButton.on = YES;
[self.switchButton addTarget:self action:@selector(switchValueChanged:) forControlEvents:UIControlEventValueChanged];

UISwitch *systemSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(120, 300, 90, 90)];
[self.view addSubview:systemSwitch];
systemSwitch.on = YES;
[systemSwitch addTarget:self action:@selector(systemSwitchValueChanged:) forControlEvents:UIControlEventValueChanged];

就这样完成了最初的目标。

总结

将来要大范围用新控件替换老控件或许也可以采取这种方式。

demo在这里

正文到此结束
Loading...