這個(gè)仿寫自今日頭條應(yīng)用內(nèi)推送提示的AlertView
篓叶,請(qǐng)先看效果圖属拾,如下
彈窗的消失有這幾種方法:
- 點(diǎn)擊右上角的關(guān)閉符號(hào)
- 左右滑動(dòng)一定范圍
- 調(diào)用系統(tǒng)的
dismiss
方法脾拆。
使用到的主要方法:
-
UIView
的spring
動(dòng)畫 - 通過(guò)
runtime
對(duì)UITapGestrueRecognize
的封裝 -
GCD
定時(shí)器
請(qǐng)看主要代碼
- 手勢(shì)操作部分
#pragma mark -- 配置手勢(shì)操作
- (void)recognizeConfigs{
__weak typeof(self) weakSelf = self;
UIPanGestureRecognizer *panGestrue = [UIPanGestureRecognizer recognizeWithAction:^(id gestrueRecognize) {
UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)gestrueRecognize;
CGPoint translation = [recognizer translationInView:self];
CGPoint viewNewPoint = CGPointMake(recognizer.view.center.x + translation.x,recognizer.view.center.y + translation.y);
CGFloat y = isIphoneX?(kScreenHeight - ( 34 + 49 + 10 + AlertHeight/2)):(kScreenHeight - (49 + 10 + AlertHeight/2));
//TODO:給拖拽控件設(shè)置范圍
viewNewPoint.y = y;//固定 y 值嫉髓,控件只能在水平方向上移動(dòng)
recognizer.view.center = viewNewPoint;
[recognizer setTranslation:CGPointZero inView:[UIApplication sharedApplication].keyWindow];
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
{
NSLog(@"手勢(shì)已經(jīng)開(kāi)始");
if (weakSelf.gestrueDidStart) {
weakSelf.gestrueDidStart();//手勢(shì)開(kāi)始時(shí),暫停定時(shí)器
}
}
break;
case UIGestureRecognizerStateEnded:
{
NSLog(@"手勢(shì)已經(jīng)結(jié)束");
if (recognizer.view.center.x < 30 || recognizer.view.center.x > kScreenWidth - 30) {
[self dismissWithNoAnimation];
if (weakSelf.gestrueDidEndWithTimerInvalidate) {//手勢(shì)結(jié)束唱遭,且 alert 消失,此時(shí)銷毀定時(shí)器
weakSelf.gestrueDidEndWithTimerInvalidate();
}
}else{
[UIView animateWithDuration:0.5
animations:^{
recognizer.view.center = CGPointMake(kScreenWidth/2, self.center.y);
}];
if (weakSelf.gestrueDidEnd) {//手勢(shì)結(jié)束呈驶,但 alert 沒(méi)有消失拷泽,此時(shí)繼續(xù)開(kāi)始計(jì)時(shí)操作
weakSelf.gestrueDidEnd();
}
}
}
break;
default:
break;
}
}];
panGestrue.cancelsTouchesInView = NO;
[self addGestureRecognizer:panGestrue];
}
- 彈窗的
dismiss
部分
#pragma mark -- 延遲執(zhí)行的 dismiss,延遲時(shí)間由使用者自己設(shè)定
+ (void)dismissAlertWithDelay:(NSTimeInterval)delayTime complete:(void (^)(void))complete{
BottomAlertView *bSelf = [BottomAlertView shareView];
__weak typeof(bSelf) weakSelf = bSelf;
/*
*每次啟動(dòng)定時(shí)器袖瞻,先銷毀之前的定時(shí)器
*/
// if (bSelf.timer) {
// dispatch_cancel(bSelf.timer);
// }
if (delayTime < 1) {
delayTime = 1;
}
/*
*GCD定時(shí)器
*/
__block NSTimeInterval userDelayTime = delayTime;
dispatch_queue_t queue = dispatch_get_main_queue();
bSelf.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(bSelf.timer, start, interval, 0);
dispatch_source_set_event_handler(bSelf.timer, ^{
NSLog(@"=== %f ===",userDelayTime);
if (userDelayTime == 0) {
// 取消定時(shí)器
dispatch_cancel(weakSelf.timer);
[weakSelf.baseView dismiss];
if (complete) {
complete();
}
}
userDelayTime--;
});
// 啟動(dòng)定時(shí)器
dispatch_resume(bSelf.timer);
[bSelf.baseView setGestrueDidStart:^{
//手勢(shì)開(kāi)始司致,暫停定時(shí)器
dispatch_suspend(weakSelf.timer);
}];
[bSelf.baseView setGestrueDidEnd:^{
//手勢(shì)結(jié)束,但 alert 沒(méi)有消失聋迎,此時(shí)啟動(dòng)定時(shí)器
dispatch_resume(weakSelf.timer);
}];
[bSelf.baseView setGestrueDidEndWithTimerInvalidate:^{
//手勢(shì)結(jié)束脂矫,alert 也已經(jīng)消失,此時(shí)銷毀定時(shí)器
dispatch_cancel(weakSelf.timer);
if (complete) {
complete();
}
}];
}
在上面砌庄,我通過(guò)block
來(lái)監(jiān)聽(tīng)手勢(shì)的狀態(tài)羹唠,通過(guò)這種狀態(tài)來(lái)控制定時(shí)器對(duì)象的暫停奕枢、開(kāi)啟以及取消。
關(guān)于NSTimer
和GCD
定時(shí)器佩微,GCD
定時(shí)器使用步驟比較復(fù)雜缝彬,但是相比NSTimer
,計(jì)時(shí)更加準(zhǔn)確哺眯,且更方便對(duì)定時(shí)器對(duì)象進(jìn)行暫停等操作谷浅。
順便說(shuō)一下,在UIView
動(dòng)畫的block
回調(diào)中為什么不需要使用weakSelf
奶卓?
在block
本身不被self
持有,而被別的對(duì)象持有,同時(shí)不產(chǎn)生循環(huán)引用的時(shí)候,就不需要使用weakself
了.最常見(jiàn)的代碼就是UIView
的動(dòng)畫代碼,我們?cè)谑褂?code>UIView的 animationWithDuration:animation
方法做動(dòng)畫的時(shí)候,并不需要使用weakself
,因?yàn)橐贸钟嘘P(guān)系是:
-
UIView
的某個(gè)負(fù)責(zé)動(dòng)畫的對(duì)象持有了block
,block
本身持有了self
- 但
self
并不持有block
,所以就沒(méi)有循環(huán)引用產(chǎn)生,因此就不需要使用weakSelf
了
有感興趣的小伙伴一疯,可以去我的 github
下載這個(gè) demo,你們也可以根據(jù)自己的需求來(lái)進(jìn)一步改寫。