UIButton点击间隔设置,开关防止被再度点击
分类:计算机编程

可以吗,明日早上刚吃完饭,屁股还没坐稳呢,CTO就跑过来问笔者,三姑凉啊、扫码购那块有bug啊,假如小编狂点、、、、、、去买单,会给作者生成尼玛一群订单,何况支付宝Infiniti重复唤起啊。。。作者心坎一齐首是拒绝的,那尼玛CTO估量是闲的,后来记挂,这种闲的没事干的客商估算还挺多,然后联想到那将近5、六12个分界面啊,我该怎么办。后来网络一找,果然一大推方案啊,个中最一劳永逸的方案正是利用我们巨大的runtime机制去实现暂劳永逸的做法。作者心中一阵纵情的闹饮,遵照方案做了二遍,果然,马到成功,啥?你们要笔者贴方案啊!其实互连网一大堆的,然则既然你们想要,那笔者就贴出来吧。多少个输入,省点时间,本着有助于跟自家一样萌萌的程序媛,作者就捐躯一下协和把。

防止二个button被频频点击(共总括了3种)

场景

当app有一点卡的时候,数次点击一样的button,日常出现,跳转了N次同样的分界面

1. 按键延时处总管件有哪些应用场景?

如果您做的是贰个含有轻微社交功用的应用程式,这类APP一般都会有像样“收藏”、“点赞”、“喜爱”的职能。

这么些功能实在载体是一个UIButton,假若您在历次顾客点赞的时候都发央求给服务器,假设有个别客户“手实惠”,在那边重新的点击,就能产生二个央浼还没回去,有一连发送出去许多少个须要。

并发这种情况,第一,大概引致服务器不须要的下压力,那简直是必定的;第二,由于您不分明央浼回调几时回来,要是顾客把那几个调整器销毁了,你的运用就恐怕奔溃。

其一境况就足以应用开关延时处总管件来轻易答对。

step1:创建个UIControl的分类

图片 1

解决办法

用运营时和归类,替换UIControl一呼百应事件,依照响应的间隔时间来决断是不是试行事件。

另一种缓和方案正是button点击事件施行时设置button的顾客交互属性,再度只谈谈设置三次点击间隔的主题素材。

2.实例分析?

像下边的demo里写的如此:

图片 2

JPBtnClickDelay

收藏那类功用的事件链是:顾客点击-->管理点击 -->发送央浼

好端端情状,客户点击开关,响应客户点击, 发送必要。

当使用延时管理未来(小编那边设定延时时间长度为1.0Second),当客商点击按键之后,响应顾客点击,可是还是不是马上发送央浼,而是先检查一下一遍点击之间时间差有没有1秒,若是有,再发送央求,若无,不发送央求。

step2:利用runtime动态的给分类绑定属性(不会?不急急,一会就交给)

第一种:每便在点击时先撤除从前的操作

详尽步骤

创设一个UIControl的分类

图片 3

![1278915-5087176d34bb4814.png]()

为了方便客人调动分化的间隔时间要求,在UIControl Custom.h文本中怒放间隔时间属性,UIControl Custom.h文本的代码为:

<pre><code>

3、动态增加方法和特性?

3.1 runtime是什么?

runtime简称运维时。OC正是运作机缘制,也正是在运营时候的片段体制,当中最根本的是消息机制。

Objective-C 的 Runtime 是三个周转时库(Runtime Library),它是四个关键使用 C 和汇编写的库,为 C 加多了样子对象的力量并创设了 Objective-C。那正是说它在类音信(Class information) 中被加载,完结全体的章程分发,方法转化,等等。Objective-C runtime 创立了装有供给的结构体,让 Objective-C 的姿首对象编制程序变为可能。

3.2 动态拉长方法和属性是何许?

比方,笔者要给一位动态增加三个“说大话逼”的天性,方法是那般的。先给人加多贰个分拣,然后在分拣里增多贰个性质。

注意,分类是特意用来增加方法的,在分拣里使用首要字@property增加属性,系统是不会帮大家生成setter-getter方法的。

据此大家要和煦完结setter-getter方法。

在setter方法里应用runtime的以下方法动态增添属性。

voidobjc_setAssociatedObject(idobject,constvoid*key,idvalue, objc_AssociationPolicy policy)

在getter方法里应用runtime的以下格局动态获取属性值。

idobjc_getAssociatedObject(idobject,constvoid*key)

3.3 方法交流是怎么?

记得大家的每一个OC对象都有一个isa指针吗?这一个isa正是指向创造实例对象的类。

目标方法保存到类里面,各个类里面都有一个方法列表。

当调用对象方法的时候,系统都会赶来那几个表里查找对应的秘诀和完成。

图片 4

办法映射表.png

所谓的方法沟通,也正是hook,正是把五个办法的兑现给换来了。就如上面那张图一,你调用eat方法的时候,就能够去找run方法的落到实处。

图片 5

hook.png

step3:关键:利用method_exchangeImplementations方法沟通函数的贯彻。

将这段代码放在你开关点击的不二诀窍中,举例:

import <UIKit/UIKit.h>

@interface UIControl (Custom)

@property (nonatomic, assign) NSTimeInterval custom_acceptEventInterval;// 能够用那么些给重新点击加间隔

@end
</pre></code>

UIControl Custom.m文本中贯彻格局交流(妥贴的做法是:先加多方法,借使方式已经存在,就替换原方法),在UIControl Custom.m文本的代码为:

<pre><code>

4.思路深入分析?

咱俩清楚UIButton承接自UIControl,UIButton的享有处管事人件的手艺都以它的父类UIControl传给它的。UIControl有那样二个情势:

// send the action. the first method is called for the event and is a point at which you can observe or override behavior. it is called repeately by the second.- sendAction:action to:(nullableid)target forEvent:(nullableUIEvent*)event;

合法的解说翻译过来是如此的:那些艺术用以传递事件新闻,是监听到事件后首先调用的格局,並且它是随着事件的重复发生而频仍调用的。

故此我们要兑现拦阻事件传递,重写那么些措施是最优解。

代码如下:

- (void)buttonClicked:(id)sender{

import "UIControl custom.h"

5.代码完结?

第一为UIControl增添创造分类,而且在.h文件里加多属性。

#import@interfaceUIControl(JPBtnClickDelay)/** 延迟时间 */@property(nonatomic)NSTimeIntervaljp_acceptEventInterval;@end

接下去来到.m文件

#import"UIControl JPBtnClickDelay.h"#import@interfaceUIControl()/** 是不是忽略点击 */@property(nonatomic)BOOLjp_ignoreEvent;@end@implementationUIControl(JPBtnClickDelay)-jp_sendAction:action to:target forEvent:event{if(self.jp_ignoreEvent)return;if(self.jp_acceptEventInterval>0) {self.jp_ignoreEvent=YES; [selfperformSelector:@selector(setJp_ignoreEvent:) withObject:@ afterDelay:self.jp_acceptEventInterval];} [selfjp_sendAction:action to:target forEvent:event];}-setJp_ignoreEvent:jp_ignoreEvent{ objc_setAssociatedObject(self,@selector(jp_ignoreEvent), @(jp_ignoreEvent), OBJC_ASSOCIATION_ASSIGN);}-jp_ignoreEvent{return[objc_getAssociatedObject(self, _cmd, boolValue];}-setJp_acceptEventInterval:(NSTimeInterval)jp_acceptEventInterval{ objc_setAssociatedObject(self,@selector(jp_acceptEventInterval), @(jp_acceptEventInterval), OBJC_ASSOCIATION_ASSIGN);}-(NSTimeInterval)jp_acceptEventInterval{return[objc_getAssociatedObject(self, _cmd) doubleValue];} load{ Method sys_Method = class_getInstanceMethod(self,@selector(sendAction:to:forEvent:)); Method add_Method = class_getInstanceMethod(self,@selector(jp_sendAction:to:forEvent:)); method_exchangeImplementations(sys_Method, add_Method);}@end

#import "UIControl HQStopMultiTap.h"@implementation UIControl (HQStopMultiTap)- (NSTimeInterval)timeInterval{ return[objc_getAssociatedObject(self,_cmd)doubleValue];}- setTimeInterval:(NSTimeInterval)timeInterval{ objc_setAssociatedObject(self,@selector(timeInterval),@(timeInterval),OBJC_ASSOCIATION_RETAIN_NONATOMIC);}//runtime动态绑定属性- setIsIgnoreEvent:isIgnoreEvent{ objc_setAssociatedObject(self,@selector(isIgnoreEvent),@(isIgnoreEvent),OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- isIgnoreEvent{ return[objc_getAssociatedObject(self,_cmd)boolValue]; }- resetState{ [self setIsIgnoreEvent:NO];}  load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SEL selA =@selector(sendAction:to:forEvent:); SEL selB =@selector(mySendAction:to:forEvent:); Method methodA =class_getInstanceMethod(self, selA); Method methodB =class_getInstanceMethod(self, selB); //将methodB的实现添加到系统方法中也就是说将methodA方法指针添加成方法methodB的。返回值表示是否添加成功 BOOL isAdd =class_addMethod(self, selA,method_getImplementation,method_getTypeEncoding; //添加成功了说明本类中不存在methodB所以此时必须将方法b的实现指针换成方法A的,否则b方法将没有实现。 if { class_replaceMethod(self, selB,method_getImplementation,method_getTypeEncoding; }else{ //添加失败了说明本类中有methodB的实现,此时只需要将methodA和methodB的IMP互换一下即可。 method_exchangeImplementations(methodA, methodB); } });}- mySendAction:action to:target forEvent:event{ if([NSStringFromClass(self.class)isEqualToString:@"UIButton"]) { self.timeInterval =self.timeInterval==0 ? 1.5:self.timeInterval; if(self.isIgnoreEvent){ return; }else if(self.timeInterval>0){ [self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval]; } } //此处methodA和methodB方法IMP互换了,实际上执行sendAction;所以不会死循环 self.isIgnoreEvent=YES; [self mySendAction:action to:target forEvent:event];}@end

//点击按键后先撤销此前的操作,再扩充需求开展的操作

import <objc/runtime.h>

@interface UIControl()

@property (nonatomic, assign) NSTimeInterval custom_acceptEventTime;

@end

@implementation UIControl (Custom)

(void)load{

Method systemMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
SEL sysSEL = @selector(sendAction:to:forEvent:);

  Method customMethod = class_getInstanceMethod(self, @selector(custom_sendAction:to:forEvent:));
  SEL customSEL = @selector(custom_sendAction:to:forEvent:);

  //添加方法 语法:BOOL class_addMethod(Class cls, SEL name, IM P imp, const char *types) 若添加成功则返回No
  // cls:被添加方法的类  name:被添加方法方法名  imp:被添加方法的实现函数  types:被添加方法的实现函数的返回值类型和参数类型的字符串
BOOL didAddMethod = class_addMethod(self, sysSEL, method_getImplementation(customMethod), method_getTypeEncoding(customMethod));

  //如果系统中该方法已经存在了,则替换系统的方法  语法:IMP class_replaceMethod(Class cls, SEL name, IMP imp,const char *types)
  if (didAddMethod) {
    class_replaceMethod(self, customSEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
  }else{
    method_exchangeImplementations(systemMethod, customMethod);

  }
  }

- (NSTimeInterval )custom_acceptEventInterval{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventInterval") doubleValue];
  }

- (void)setCustom_acceptEventInterval:(NSTimeInterval)custom_acceptEventInterval{
    objc_setAssociatedObject(self, "UIControl_acceptEventInterval", @(custom_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  }

- (NSTimeInterval )custom_acceptEventTime{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventTime") doubleValue];

}

- (void)setCustom_acceptEventTime:(NSTimeInterval)custom_acceptEventTime{
    objc_setAssociatedObject(self, "UIControl_acceptEventTime", @(custom_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  }

- (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{

      // 如果想要设置统一的间隔时间,可以在此处加上以下几句
      // 值得提醒一下:如果这里设置了统一的时间间隔,会影响UISwitch,如果想统一设置,又不想影响UISwitch,建议将UIControl分类,改成UIButton分类,实现方法是一样的
      // if (self.custom_acceptEventInterval <= 0) {
      //     // 如果没有自定义时间间隔,则默认为2秒
      //    self.custom_acceptEventInterval = 2;
      // }

    // 是否小于设定的时间间隔
     BOOL needSendAction = (NSDate.date.timeIntervalSince1970 - self.custom_acceptEventTime >= self.custom_acceptEventInterval);

       // 更新上一次点击时间戳
          if (self.custom_acceptEventInterval > 0) {
              self.custom_acceptEventTime = NSDate.date.timeIntervalSince1970;
           }

       // 两次点击的时间间隔小于设定的时间间隔时,才执行响应事件
           if (needSendAction) {
               [self custom_sendAction:action to:target forEvent:event];
           }

}

@end
</pre></code>

简书的二次试水,文章参谋自简书ocarol

6. 分类的使用?

此间有三个UIButton的实例对象:

[self.normalBtnaddTarget:selfaction:@selector(normalBtnClick) forControlEvents:UIControlEventTouchUpInside];[self.delayBtnaddTarget:selfaction:@selector(delayBtnClick) forControlEvents:UIControlEventTouchUpInside];self.delayBtn.jp_acceptEventInterval=1.0f;

normalBtn没有必要有延时,就怎样也不用管,就和动用系统原生的等同。

delayBtn要求延时,给它的jp_acceptEventInterval设定三个延时值,它自动就能卓有成效。

分拣代码就贴到这里了。可是,小三嫂是这种只想修复bug的人么?当然得明白他骨子里的传说,对不对。首先这么些观念相对不是自己想出来的,小编只是看完外人贴的代码以往,做了点本人的思虑,接待各位小小叔子们来喷,但是,请手下留情。首先runtime动态绑定属性也没怎么好剖判的。主要也正是objc_setAssociatedObject/objc_getAssociatedObject那五个法子。那那多少个性情timeInterval 代表多久内不可能被两次三番点击isIgnoreEvent代表有些button是还是不是不供给这种体制,能够和煦安装。其实重要的思虑依旧交换方法的兑现。对于一个加以的平地风波,UIControl会调用sendAction:to:forEvent:方法,那我们就足以写叁个投机的艺术,让顾客点击button的时候实在施行的是大家团结的不二秘籍,在我们本人的格局里面再去调用自个儿的方法(因为方法交流了扳平去调用了系统的法子),可是保障顾客后一次点击的时候不会再开展轮换,那尼玛就瞎了,又换回去了,搞毛啊。所以,用static dispatch_once_t onceToken;

[[selfclass]cancelPreviousPerformRequestsWithTarget:selfselector:@selector(buttonClicked:)object:sender];

7. Demo下载?

请点击这里去往Github。

dispatch_once(&onceToken, ^{

[selfperformSelector:@selector(buttonClicked: )withObject:senderafterDelay:0.2f];

}保障只会换换二遍。OK?哎,近日小四妹小编也在钻探runtime,好高深的标准,迎接各位小四哥来请教。对了,还恐怕有啥样难题也足以给自个儿留言,小编尽量正经的答问你。。。

}

第两种:点击后设为不可被点击的景观,几秒后复原:

-(void)buttonClicked:(id)sender{

self.button.enabled =NO;

[selfperformSelector:@selector(changeButtonStatus)withObject:nilafterDelay:1.0f];//幸免重复点击

}

-(void)changeButtonStatus{

self.button.enabled =YES;

}

其三种:使用runtime,一劳永逸小编那设的是0.5秒内不会被重新点击

1.导入objc / runtime.h(能够放在PCH文件里)

2.创办uicontrol或UIButton的的分类!

创制分类文件:

2.1 展开Xcode中,新建文件,选拔OC文件

2.2 在其次个界面,File名字为UIControl UIControl_buttonCon,将文件类型File Type选为Category类,在类里选承袭的等级次序,这里大家选的Class是UIButton

注:若用Unbutton分类,则会对对Unbutton成立的开关反应。

2.3 分类创制完结对分类开展操作

.h文件

#import

#define defaultInterval.5//暗许时间间隔

@interfaceUIControl (UIControl_buttonCon)

@property(nonatomic,assign)NSTimeIntervaltimeInterval;//用这几个给重新点击加间隔

@property(nonatomic,assign)BOOLisIgnoreEvent;//YES差异意点击NO允许点击

@end

.m文件

#import"UIControl UIControl_buttonCon.h"

@implementationUIControl (UIControl_buttonCon)

- (NSTimeInterval)timeInterval{

return[objc_getAssociatedObject(self,_cmd)doubleValue];

}

- (void)setTimeInterval:(NSTimeInterval)timeInterval{

objc_setAssociatedObject(self,@selector(timeInterval),@(timeInterval),OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

//runtime动态绑定属性

- (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{

objc_setAssociatedObject(self,@selector(isIgnoreEvent),@(isIgnoreEvent),OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (BOOL)isIgnoreEvent{

return[objc_getAssociatedObject(self,_cmd)boolValue];

}

- (void)resetState{

[selfsetIsIgnoreEvent:NO];

}

 (void)load{

staticdispatch_once_tonceToken;

dispatch_once(&onceToken, ^{

SELselA =@selector(sendAction:to:forEvent:);

SELselB =@selector(mySendAction:to:forEvent:);

MethodmethodA =class_getInstanceMethod(self, selA);

MethodmethodB =class_getInstanceMethod(self, selB);

//将methodB的贯彻增加到系统方法中也正是说将methodA方法指针增添成方法methodB的再次来到值表示是还是不是丰裕成功

BOOLisAdd =class_addMethod(self, selA,method_getImplementation(methodB),method_getTypeEncoding(methodB));

//加多成功了印证本类中不真实methodB所以此时必得将艺术b的兑现指针换来方法A的,不然b方法将尚未完成。

if(isAdd) {

class_replaceMethod(self, selB,method_getImplementation(methodA),method_getTypeEncoding(methodA));

}else{

//增加停业了验证本类中有methodB的兑现,此时只须求将methodA和methodB的IMP调换一下就可以。

method_exchangeImplementations(methodA, methodB);

}

});

}

- (void)mySendAction:(SEL)action to:(id)target forEvent:(UIEvent*)event{

if([NSStringFromClass(self.class)isEqualToString:@"UIButton"]) {

self.timeInterval=self.timeInterval==0?defaultInterval:self.timeInterval;

if(self.isIgnoreEvent){

return;

}elseif(self.timeInterval>0){

[selfperformSelector:@selector(resetState)withObject:nilafterDelay:self.timeInterval];

}

}

//此处methodA和methodB方法IMP交流了,实际上施行sendAction;所以不会死循环

self.isIgnoreEvent=YES;

[selfmySendAction:actionto:targetforEvent:event];

}

@end

本文由pc28.am发布于计算机编程,转载请注明出处:UIButton点击间隔设置,开关防止被再度点击

上一篇:UIPickerView的常见属性 下一篇:没有了
猜你喜欢
热门排行
精彩图文
  • iOS8扩展插件开发配置
    iOS8扩展插件开发配置
    Share Extension 简介 前两篇小说介绍了Application Extension运维原理、TodayExtension,本篇来介绍一下 Share Extension创制和动用。分享扩大给用提供二个有利的不二秘
  • Application Extension(二):Today Extension
    Application Extension(二):Today Extension
    简介 在公告中央的Today的视图中显得的 extension 叫做 widget ,widget能够一本万利客户神速的获取想要的音信,不用再经过复杂的步骤伸开app工夫找到自个儿想
  • Application Extension(一):介绍
    Application Extension(一):介绍
    本篇文章是对 iOS Application Extension的简单介绍。介绍的开始和结果富含:对Application Extension的品类、运营原理、申明周期、之间的通讯等。 转发请注脚出处
  • 内购模块,中沙盒账号使用注意事项
    内购模块,中沙盒账号使用注意事项
    重新提醒登入账号.png https://wilddylan.github.io/2016/09/23/IAP/ 文档: 京东开拓 1、前言 在iOS12前,借使对沙盒账号测量试验充钱不熟知的同学,大概会境遇过如此
  • 自家是怎样一步一步完结网页离线缓存的,缓存
    自家是怎样一步一步完结网页离线缓存的,缓存
    在利用iOS的webview的时候开采这么八个难题,加载二个网页进来,webview会负担缓存页面里的css,js和图片那么些财富。可是这一个缓存不受开拓者调控,缓存