问题描述
这个问题是我在用自定义点选按钮替换系统的UISwitch时遇到的。
如图,右边的两个按钮就是自定义点选按钮,分别对应之前UISwitch的开启和关闭状态:
我的目标
全局搜索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];
就这样完成了最初的目标。
总结
将来要大范围用新控件替换老控件或许也可以采取这种方式。