为制作制作iOS悬浮可拖动、吸附屏幕边缘的按钮,网上搜索到了一个古老的第三方类方法(如下)。
导入我的工程后,出现了tabbar、拍照按钮等失效或响应非常慢的情况。究竟是什么出错了呢?请先看这位同学写的源代码:
设计UIButton的分类:UIButton+NMCategory.h:
@interface UIButton (NMCategory)
@property(nonatomic,assign,getter = isDragEnable) BOOL dragEnable;
@property(nonatomic,assign,getter = isAdsorbEnable) BOOL adsorbEnable;
@end
分类的实现:
#import "UIButton+NMCategory.h"
#import <objc/runtime.h>
#define PADDING 5
static void *DragEnableKey = &DragEnableKey;
static void *AdsorbEnableKey = &AdsorbEnableKey;
@implementation UIButton (NMCategory)
-(void)setDragEnable:(BOOL)dragEnable
{
objc_setAssociatedObject(self, DragEnableKey,@(dragEnable), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL)isDragEnable
{
return [objc_getAssociatedObject(self, DragEnableKey) boolValue];
}
-(void)setAdsorbEnable:(BOOL)adsorbEnable
{
objc_setAssociatedObject(self, AdsorbEnableKey,@(adsorbEnable), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL)isAdsorbEnable
{
return [objc_getAssociatedObject(self, AdsorbEnableKey) boolValue];
}
CGPoint beginPoint;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.highlighted = YES;
if (![objc_getAssociatedObject(self, DragEnableKey) boolValue]) {
return;
}
UITouch *touch = [touches anyObject];
beginPoint = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
self.highlighted = NO;
if (![objc_getAssociatedObject(self, DragEnableKey) boolValue]) {
return;
}
UITouch *touch = [touches anyObject];
CGPoint nowPoint = [touch locationInView:self];
float offsetX = nowPoint.x - beginPoint.x;
float offsetY = nowPoint.y - beginPoint.y;
self.center = CGPointMake(self.center.x + offsetX, self.center.y + offsetY);
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.highlighted) {
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
self.highlighted = NO;
}
if (self.superview && [objc_getAssociatedObject(self,AdsorbEnableKey) boolValue] ) {
float marginLeft = self.frame.origin.x;
float marginRight = self.superview.frame.size.width - self.frame.origin.x - self.frame.size.width;
float marginTop = self.frame.origin.y;
float marginBottom = self.superview.frame.size.height - self.frame.origin.y - self.frame.size.height;
[UIView animateWithDuration:0.125 animations:^(void){
if (marginTop<60) {
self.frame = CGRectMake(marginLeft<marginRight?marginLeft<PADDING?PADDING:self.frame.origin.x:marginRight<PADDING?self.superview.frame.size.width -self.frame.size.width-PADDING:self.frame.origin.x,
PADDING,
self.frame.size.width,
self.frame.size.height);
}
else if (marginBottom<60) {
self.frame = CGRectMake(marginLeft<marginRight?marginLeft<PADDING?PADDING:self.frame.origin.x:marginRight<PADDING?self.superview.frame.size.width -self.frame.size.width-PADDING:self.frame.origin.x,
self.superview.frame.size.height - self.frame.size.height-PADDING,
self.frame.size.width,
self.frame.size.height);
}
else {
self.frame = CGRectMake(marginLeft<marginRight?PADDING:self.superview.frame.size.width - self.frame.size.width-PADDING,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height);
}
}];
}
}
@end
在要实现的viewController里,添加一个window成员变量:
@property (strong, nonatomic) UIWindow *window;
在VC里放入按钮:
//可拖动、自动贴近边缘的按钮
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(5, 100,60, 60)];
btn.backgroundColor = [UIColor redColor];
btn.tag = 0;
btn.layer.cornerRadius = 8;
[btn setDragEnable:YES];
[btn setAdsorbEnable:YES];
[self.view addSubview:btn];
[btn addTarget:self action:@selector(showTag:) forControlEvents:UIControlEventTouchUpInside];
这里,showTag: 作为点击按钮后触发的事件,可以按照自己的需要改写。
先不说古老的编程方式(@synthesize应该不再用了,新版编译器的可以直接交由@property完成了)……这个写得有个致命的错误,各位新手要引以为戒: 在调用touchesBegan等方法时,没有先调用[super touchesBegan:] 。也就是说,在扩展类中不先调用父类方法,而是直接重写,这会导致父类方法会调用,效果上相当于直接覆盖父类方法,这就导致UIButton原本的touchesBegan等方法的效果不再体现。所以很多按钮都发生了问题。
这个错例告诉我们在做类扩展、分类、继承时必须要注意的事项。
那么iOS悬浮按钮的稳妥制作方法是什么呢?请查看本博客另一篇博文。