ios UIButton点击范围设置 2021-04-13
2021-04-16 本文已影响0人
DSQ
可以自己写UIButton分类重写按钮响应链方法,结合关联对象设置属性来控制button的具体实现方法。
- hitTest中的内部实现面试时可能会用到
// 重新实现hittest方法,实现圆形按钮点击范围
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.userInteractionEnabled||self.hidden||self.alpha<0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
// 遍历当前对象的子视图
__block UIView *hit = nil;
[self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 坐标转换
// convertPoint: toView:是(调用者的point+convertpoint)- toview的point
// convertPoint: fromView:是(fromView的point+convertpoint)- 调用者的point
CGPoint convertPoint = [self convertPoint:point toView:obj];
// 调用子视图的hittest方法
hit = [obj hitTest:convertPoint withEvent:event];
// 如果找到了接受事件的对象,就停止遍历
if (hit) {
*stop=YES;
}
}];
if (hit) {
return hit;
}else{
return self;
}
}else{
return nil;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// 点击的点
CGFloat x1=point.x;
CGFloat y1=point.y;
// 中心
CGFloat x2=self.frame.size.width/2;
CGFloat y2=self.frame.size.height/2;
// 平方差公式
double dis = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
if (dis<=self.frame.size.width/2) {
return YES;
}else{
return NO;
}
}
针对超出父view之外的部分,比如按钮点击无响应问题
原因:响应链会先判断点是否在父view范围内,在父view范围内就正序遍历子视图判断哪个来响应,不在就不响应该点击事件。
//超出父层的按钮部分点击无效果 --- 可通过父类view重写如下方法判断子view能否响应
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView *subV in self.subviews) {
if ([subV isKindOfClass:[UIButton class]]) {
UIButton *btn=(UIButton *)subV;
if (CGRectContainsPoint(btn.frame, point)) {
return YES;
}
}
}
BOOL ss=[super pointInside:point withEvent:event];
return ss;
}
在来个CoreAnimation运动动画的btn无法点击办法解决:
自定义子view和自定义父view分别重写- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event和- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法
自定义父view实现:
//超出父层的按钮部分点击无效果 --- 可通过重写如下方法盘点子view能否响应时间
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.userInteractionEnabled||self.hidden||self.alpha<0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
// 遍历当前对象的子视图
__block UIView *hit = nil;
[self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 看情需要否转换坐标
// 坐标转换
// convertPoint: toView:是(调用者的point+convertpoint)- toview的point
// convertPoint: fromView:是(fromView的point+convertpoint)- 调用者的point
// CGPoint convertPoint = [self convertPoint:point toView:obj];
// 调用子视图的hittest方法
hit = [obj hitTest:point withEvent:event];
// 如果找到了接受事件的对象,就停止遍历
if (hit) {
*stop=YES;
}
}];
if (hit) {
return hit;
}else{
return self;
}
}else{
return nil;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView *subV in self.subviews) {
if ([subV isKindOfClass:[UIButton class]]) {
UIButton *btn=(UIButton *)subV;
// 通过判断子view的运动layerframe来确定点击范围
if (CGRectContainsPoint(btn.layer.presentationLayer.frame, point) ) {
return YES;
}
}
}
BOOL ss=[super pointInside:point withEvent:event];
return ss;
}
自定义子view实现:
// 通过判断子view的运动layerframe来确定点击范围
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *vc=[super hitTest:point withEvent:event];
if (CGRectContainsPoint(self.layer.presentationLayer.frame, point) ) {
return self;
}
return vc;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL ss=NO;
if (CGRectContainsPoint(self.layer.presentationLayer.frame, point) ) {
ss=YES;
}else{
ss=[super pointInside:point withEvent:event];
}
return ss;
}
最后送个仿支付宝上下不规则浮动泡泡的动画:
CABasicAnimation *opacityAnim1 =[CABasicAnimation animationWithKeyPath:@"opacity"];
opacityAnim1.fromValue = [NSNumber numberWithDouble:1];
opacityAnim1.toValue =[NSNumber numberWithDouble:.5];
opacityAnim1.duration=5+(double)arc4random()/0x100000000;// 修改渐变动画时间
opacityAnim1.repeatCount=MAXFLOAT;
opacityAnim1.delegate=self;
opacityAnim1.fillMode =kCAFillModeForwards;
opacityAnim1.autoreverses=YES;
opacityAnim1.removedOnCompletion=NO;
opacityAnim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[btn.layer addAnimation:opacityAnim1 forKey:@"LightopacityAnim"];
CABasicAnimation *shake1Animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
double fw1=50+(double)arc4random()/0x100000000;// 修改位移动画距离
shake1Animation.duration=5+(double)arc4random()/0x100000000;// 修改位移动画时间
shake1Animation.fromValue = [NSNumber numberWithFloat:-fw1];
shake1Animation.toValue = [NSNumber numberWithFloat:fw1];
shake1Animation.autoreverses = YES;
shake1Animation.repeatCount=MAXFLOAT;
shake1Animation.fillMode =kCAFillModeForwards;
shake1Animation.removedOnCompletion=NO;
shake1Animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[btn.layer addAnimation:shake1Animation forKey:nil];