項(xiàng)目中常會(huì)遇到在按鈕的點(diǎn)擊事件中去執(zhí)行一些耗時(shí)操作。如果處理不當(dāng)經(jīng)常會(huì)出現(xiàn)連續(xù)多次點(diǎn)擊push多次的情況,造成不好的用戶體驗(yàn)。 怎么解決

在日常開發(fā)中經(jīng)常會(huì)碰到一種bug就是因?yàn)橛脩艨焖冱c(diǎn)擊某個(gè)按鈕,導(dǎo)致頁面重復(fù)push或者重復(fù)發(fā)送網(wǎng)絡(luò)請(qǐng)求。這樣的問題既對(duì)用戶體驗(yàn)有影響,而且還會(huì)一定程度上增加服務(wù)器的壓力。

目前,我為了防止按鈕快速點(diǎn)擊主要使用以下兩種辦法

1.在每次點(diǎn)擊時(shí)先取消之前的操作(網(wǎng)上看到的方法)

[cpp]?view plain?copy

-?(void)buttonClicked:(id)sender??

{??

//這里是關(guān)鍵,點(diǎn)擊按鈕后先取消之前的操作,再進(jìn)行需要進(jìn)行的操作??

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

??[self?performSelector:@selector(buttonClicked:?)withObject:sender?afterDelay:0.2f];??

}??

2.點(diǎn)擊后將按鈕置為不可點(diǎn)擊狀態(tài),幾秒后恢復(fù)

[cpp]?view plain?copy

-(void)buttonClicked:(id)sender{??

??self.button.enabled?=?NO;??

[self?performSelector:@selector(changeButtonStatus)?withObject:nil?afterDelay:1.0f];//防止用戶重復(fù)點(diǎn)擊??

}??


-(void)changeButtonStatus{??

??self.button.enabled?=?YES;??

}??

或者使用:

[cpp]?view plain?copy

-?(void)?timeEnough??

{??


UIButton?*btn=(UIButton*)[self.view?viewWithTag:33];??


btn.selected=NO;???

[timer?invalidate];??


timer=nil;???

}??


-?(void)?btnDone:(UIButton*)btn??

?{??

if(btn.selected)?return;??

?btn.selected=YES;??

[self?performSelector:@selector(timeEnough)?withObject:nil?afterDelay:3.0];//使用延時(shí)進(jìn)行限制。??

//to?do?something.??

}??

iOS客戶端經(jīng)常遇到點(diǎn)擊某個(gè)按鈕發(fā)送一個(gè)請(qǐng)求到服務(wù)器,貌似一個(gè)非常簡(jiǎn)單的需求有的時(shí)候其實(shí)并不是那么簡(jiǎn)單,比如網(wǎng)絡(luò)不好的時(shí)候,用戶重復(fù)點(diǎn)擊一個(gè)按鈕會(huì)發(fā)送多次請(qǐng)求,比如在我負(fù)責(zé)的客戶端來說用戶發(fā)帖功能導(dǎo)致的弊端就是,一個(gè)用戶對(duì)一個(gè)帖子回復(fù)了很多條,有的時(shí)候甚至達(dá)到了10多條,如何解決這一的問題呢。方案其實(shí)有很多。

利用MBProgressHud等控件

眾所周知MBProgressHud或者SVProgresHud經(jīng)常被利用在項(xiàng)目中,主要是在網(wǎng)絡(luò)請(qǐng)求發(fā)起到網(wǎng)絡(luò)相應(yīng)收到的這段時(shí)間在客戶端形成一個(gè)遮罩,可以用來阻止用戶點(diǎn)擊UI進(jìn)行操作,防止某些意外的請(qǐng)求產(chǎn)生。

優(yōu)點(diǎn):解決了用戶重復(fù)點(diǎn)擊多次發(fā)送請(qǐng)求的問題,同時(shí)防止了在某些條件不具備的情況進(jìn)行其他操作引發(fā)客戶端出現(xiàn)問題的出現(xiàn)。

缺點(diǎn):有的時(shí)候不人性化,比如用戶進(jìn)入某個(gè)界面就是網(wǎng)速不好,一直請(qǐng)求數(shù)據(jù),等了好長(zhǎng)時(shí)間都沒有結(jié)果,這個(gè)時(shí)候用戶一般都會(huì)下意識(shí)點(diǎn)擊返回按鈕,但是這種情況下,返回按鈕的點(diǎn)擊事件也是不起作用的。

利用運(yùn)行時(shí)設(shè)置相應(yīng)按鈕點(diǎn)擊間隔

1. 對(duì)UIControl進(jìn)行擴(kuò)展

該方案來自http://www.cocoachina.com/ios/20150828/13260.html

@interfaceUIControl(delay)@property(nonatomic,assign)NSTimeIntervaluxy_acceptEventInterval;// 可以用這個(gè)給重復(fù)點(diǎn)擊加間隔@end#import"UIControl+delay.h"#import//增加兩個(gè)屬性staticconstchar*UIControl_acceptEventInterval="UIControl_acceptEventInterval";staticconstchar*UIControl_ignoreEvent="UIControl_ignoreEvent";@implementationUIControl(delay)//時(shí)間間隔- (NSTimeInterval)uxy_acceptEventInterval{return[objc_getAssociatedObject(self,UIControl_acceptEventInterval) doubleValue];}- (void)setUxy_acceptEventInterval:(NSTimeInterval)uxy_acceptEventInterval{? ? objc_setAssociatedObject(self,UIControl_acceptEventInterval, @(uxy_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}//是否響應(yīng)事件的標(biāo)志位-(BOOL)uxy_ignoreEvent{return[objc_getAssociatedObject(self,UIControl_ignoreEvent) boolValue];}-(void)setUxy_ignoreEvent:(BOOL)uxy_ignoreEvent{? ? objc_setAssociatedObject(self,UIControl_ignoreEvent, @(uxy_ignoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}+(void)load{//將系統(tǒng)的sendAction方法和自己實(shí)現(xiàn)的方法進(jìn)行互換Method a=class_getInstanceMethod(self,@selector(sendAction:to:forEvent:));? ? Method b = class_getInstanceMethod(self,@selector(__uxy_sendAction:to:forEvent:));? ? method_exchangeImplementations(a,b);}//點(diǎn)擊后會(huì)先進(jìn)入這里- (void)__uxy_sendAction:(SEL)action to:(id)target forEvent:(UIEvent*)event{if(self.uxy_ignoreEvent)//根據(jù)狀態(tài)判斷是否繼續(xù)執(zhí)行return;if(self.uxy_acceptEventInterval >0)? ? {self.uxy_ignoreEvent =YES;//周期性清空標(biāo)志位[selfperformSelector:@selector(setUxy_ignoreEvent:) withObject:@(NO) afterDelay:self.uxy_acceptEventInterval];? ? }//這里其實(shí)是系統(tǒng)的原來的sendAction to方法。[self__uxy_sendAction:action to:target forEvent:event];}@end

2.對(duì)UIButton進(jìn)行擴(kuò)展

該方案來自http://www.tuicool.com/articles/NJvmIf

這個(gè)在點(diǎn)擊UITabbar上的按鈕時(shí)會(huì)崩潰,提示

-[UITabBarButton cs_acceptEventTime]: unrecognized selector sent to instance 0x7fc9d8f36c50,自己找了好久都沒有找到原因,后來參考

http://blog.jobbole.com/79580/改寫了load方法就好了,原因不明白一直不明白,UIButton繼承UIControl應(yīng)該沒有什么問題,為什么UITabbarButton會(huì)出錯(cuò)呢。方案一對(duì)UIControl進(jìn)行擴(kuò)展,在load方法里面直接進(jìn)行了交換,是因?yàn)閁IControl的sendAction:to:event方法確實(shí)是存在的,也許UITabbarButton有特殊的地方吧,就是沒有這個(gè)方法。

@implementationUIButton(delay)// 因category不能添加屬性,只能通過關(guān)聯(lián)對(duì)象的方式。staticconstchar*UIControl_acceptEventInterval="UIControl_acceptEventInterval";- (NSTimeInterval)cs_acceptEventInterval {return[objc_getAssociatedObject(self,UIControl_acceptEventInterval) doubleValue];}- (void)setCs_acceptEventInterval:(NSTimeInterval)cs_acceptEventInterval {? ? objc_setAssociatedObject(self,UIControl_acceptEventInterval, @(cs_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}staticconstchar*UIControl_acceptEventTime="UIControl_acceptEventTime";- (NSTimeInterval)cs_acceptEventTime {return[objc_getAssociatedObject(self,UIControl_acceptEventTime) doubleValue];}- (void)setCs_acceptEventTime:(NSTimeInterval)cs_acceptEventTime {? ? objc_setAssociatedObject(self,UIControl_acceptEventTime, @(cs_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}// 在load時(shí)執(zhí)行hook+ (void)load {staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{? ? ? ? Classclass= [selfclass];//分別獲取SEL beforeSelector =@selector(sendAction:to:forEvent:);? ? ? ? SEL afterSelector =@selector(cs_sendAction:to:forEvent:);? ? ? ? ? ? ? ? Method beforeMethod = class_getInstanceMethod(class, beforeSelector);? ? ? ? Method afterMethod = class_getInstanceMethod(class, afterSelector);//先嘗試給原來的方法添加實(shí)現(xiàn),如果原來的方法不存在就可以添加成功。返回為YES,否則//返回為NO。//UIButton 真的沒有sendAction方法的實(shí)現(xiàn),這是繼承了UIControl的而已,UIControl才真正的實(shí)現(xiàn)了。BOOLdidAddMethod =? ? ? ? class_addMethod(class,? ? ? ? ? ? ? ? ? ? ? ? beforeSelector,? ? ? ? ? ? ? ? ? ? ? ? method_getImplementation(afterMethod),? ? ? ? ? ? ? ? ? ? ? ? method_getTypeEncoding(afterMethod));NSLog(@"%d",didAddMethod);if(didAddMethod) {// 如果之前不存在,但是添加成功了,此時(shí)添加成功的是cs_sendAction方法的實(shí)現(xiàn)// 這里只需要方法替換class_replaceMethod(class,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? afterSelector,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? method_getImplementation(beforeMethod),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? method_getTypeEncoding(beforeMethod));? ? ? ? }else{//本來如果存在就進(jìn)行交換method_exchangeImplementations(afterMethod, beforeMethod);? ? ? ? }? ? });}- (void)cs_sendAction:(SEL)action to:(id)target forEvent:(UIEvent*)event {if([NSDatedate].timeIntervalSince1970 -self.cs_acceptEventTime 0) {self.cs_acceptEventTime = [NSDatedate].timeIntervalSince1970;? ? }? ? [selfcs_sendAction:action to:target forEvent:event];? ? }@end

優(yōu)點(diǎn):有效解決了用戶雙擊UI造成事件觸發(fā)兩次的情況(不僅僅局限網(wǎng)絡(luò)請(qǐng)求)/

缺點(diǎn) :在網(wǎng)絡(luò)不好的情況下,很可能在m秒內(nèi)確實(shí)沒有收到服務(wù)器響應(yīng)。如果用戶一直點(diǎn)擊按鈕,很可能觸發(fā)重復(fù)點(diǎn)擊。而且可能和系統(tǒng)以及存在的事件沖突,有的時(shí)候會(huì)產(chǎn)生莫名其妙的錯(cuò)誤。比如我加入這個(gè)類擴(kuò)展后,項(xiàng)目中選擇照片時(shí)候進(jìn)行拍照上傳的時(shí)候,本來需要點(diǎn)擊一下拍攝按鈕就可以成功的事情,確需要長(zhǎng)時(shí)間觸摸才能生效,所以這個(gè)方案待改進(jìn)。

客戶端網(wǎng)絡(luò)請(qǐng)求方法中過濾

一個(gè)網(wǎng)絡(luò)請(qǐng)求包含兩部分:url和參數(shù),因此我們可以在網(wǎng)絡(luò)請(qǐng)求方類里面增加一個(gè)NSMutableArray,用戶url和參數(shù)的md5進(jìn)行一次加密作為key,發(fā)送之前我們可以對(duì)其值賦值為任意固定值,當(dāng)服務(wù)器返回結(jié)果的時(shí)候我們可以將這個(gè)鍵值對(duì)移除。每次發(fā)送網(wǎng)絡(luò)請(qǐng)求前,先從這個(gè)字典中查看本次請(qǐng)求的md5值是否存在,如果存在表明本次請(qǐng)求已經(jīng)發(fā)送但是尚未收到響應(yīng),此時(shí)應(yīng)該return,不再進(jìn)行網(wǎng)絡(luò)請(qǐng)求,否則就是收到響應(yīng)了或者該請(qǐng)求是第一次發(fā)出,改方法貌似不錯(cuò)。

注意:有的時(shí)候參數(shù)包含了時(shí)間戳,這樣計(jì)算永遠(yuǎn)會(huì)不相同的,md5加密之前要清除參數(shù)中的時(shí)間戳或者隨機(jī)字段。

交給服務(wù)器解決

上面的辦法都是客戶端進(jìn)行解決的,其實(shí)仔細(xì)想想這個(gè)問題服務(wù)器端難道就能完全沒有責(zé)任嗎?顯然不是! 比如有人惡意模仿客戶端模擬頻繁向服務(wù)器發(fā)出http請(qǐng)求,這勢(shì)必會(huì)造成服務(wù)器端資源浪費(fèi),雖然說http協(xié)議是不能記住狀態(tài)的(需要靠session技術(shù)實(shí)現(xiàn)),但是服務(wù)器對(duì)這樣的行為就束手無策,顯然是不符合常理的。介于本人對(duì)服務(wù)器的技術(shù)了解有限,所以感覺應(yīng)該上一種解決方案里面的客戶端實(shí)現(xiàn)的過濾加入到服務(wù)器端實(shí)現(xiàn),基本和客戶端一致。

具體方案參考:

服務(wù)器把每次把收到的請(qǐng)求進(jìn)行MD5加密,作為一個(gè)字典的鍵,值可以設(shè)置任意,然后查找數(shù)據(jù)庫,查找回來以后通過適當(dāng)?shù)男问椒祷乜蛻舳耍诓檎覕?shù)據(jù)期間,收到請(qǐng)求先從字典查找鍵是否存在如果已經(jīng)存在就不作出響應(yīng),因?yàn)檎诓檎抑校駝t操作數(shù)據(jù)庫查找數(shù)據(jù),并且將鏈接鍵入到字典里面。

作者:鴻雁長(zhǎng)飛光不度

鏈接:http://www.lxweimin.com/p/a830d0a57378

來源:簡(jiǎn)書

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,312評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,410評(píng)論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,778評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,955評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,521評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,266評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,468評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,696評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評(píng)論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,193評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,431評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容