IU
问题描述
这个问题是我在用自定义点选按钮替换系统的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];
就这样完成了最初的目标。
总结
将来要大范围用新控件替换老控件或许也可以采取这种方式。