前言
相信很多人都會選擇自定義alertView,網(wǎng)上也有太多大神封裝了類似的三方庫笛钝,但用來用去质况,感覺最靠譜的,還是系統(tǒng)的玻靡,這也是之前花功夫針對系統(tǒng)的alert進(jìn)行適配封裝的原因之一(iOS (封裝)一句話調(diào)用系統(tǒng)的alertView和alertController)结榄。
但是系統(tǒng)的效果畢竟是局限的,很多時候囤捻,我們僅僅是需要顯示一個遮罩層的提示語臼朗,又或者是比較麻煩的,需要顯示一個可實(shí)現(xiàn)多種交互的提示窗蝎土,這時视哑,還是得自定義……
下面的封裝思路,相對來說簡單一些誊涯,但肯定不是最好的挡毅,甚至因?yàn)椴坏貌坏脑颍昧藛卫@個東西暴构,如果你有更好的改善方法跪呈,還望多多指教段磨。
代碼詳見GitHub:Demo_JXTAlertView
16.3.8更新Demo,添加了模態(tài)跳轉(zhuǎn)視圖控制器實(shí)現(xiàn)alertView彈出
代碼封裝度不高庆械,只是提供一個簡單的實(shí)現(xiàn)思路薇溃。
下面是演示效果(定義的樣式很簡單菌赖,因?yàn)間if幀數(shù)限制缭乘,動畫效果被削弱了):
1.彈性動畫
iOS的動畫效果是很強(qiáng)悍的,彈窗展示時琉用,需要一個彈性動畫去過渡堕绩,彈性動畫的實(shí)現(xiàn)方式有很多,也比較簡單邑时,但難的是自然平滑的效果奴紧,下面的兩種實(shí)現(xiàn),是參考了網(wǎng)上的例子晶丘,兩種方法大同小異黍氮,代碼還是很好理解的,具體參數(shù)可自行調(diào)整:
- 1.方式一:
- (void)shakeToShow:(UIView *)aView
{
CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
animation.duration = 0.2;
NSMutableArray * values = [NSMutableArray array];
[values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)]];
[values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)]];
[values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.9, 0.9, 1.0)]];
[values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 1.0)]];
animation.values = values;
[aView.layer addAnimation:animation forKey:nil];
}
- 2.方式二:
- (void)shakeToShow:(UIView *)aView
{
CAKeyframeAnimation *popAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
popAnimation.duration = 0.35;
popAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.01f, 0.01f, 1.0f)],
[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1f, 1.1f, 1.0f)],
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.9f, 0.9f, 1.0f)],
[NSValue valueWithCATransform3D:CATransform3DIdentity]];
popAnimation.keyTimes = @[@0.0f, @0.5f, @0.75f, @1.0f];
popAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[aView.layer addAnimation:popAnimation forKey:nil];
}
我采用了第二種方式浅浮,相對來說沫浆,過渡更為順滑些,如果想要實(shí)現(xiàn)和系統(tǒng)的alert動畫相似的效果滚秩,可以去掉values
數(shù)組中的倒數(shù)第二項(xiàng)专执,當(dāng)然keyTimes
和timingFunctions
也要去掉對應(yīng)的項(xiàng)。
2.全屏遮罩
一般這類提示窗是顯示在一個半透明黑的遮罩層上的郁油,遮罩層的實(shí)現(xiàn)也有多種方式本股,我采用的是在keyWindow層上添加一個和屏幕尺寸相當(dāng)?shù)陌胪该骱诘膙iew:
_alertBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)];
_alertBackgroundView.backgroundColor = UIColorFromHEX(0x000000, 0.7);
[[UIApplication sharedApplication].keyWindow addSubview:_alertBackgroundView];
有些人習(xí)慣設(shè)置view的alpha值,但這樣做桐腌,會導(dǎo)致其子視圖也會半透明化拄显,最簡單的還是設(shè)置背景色的透明度,這里采用的是16進(jìn)制色值案站,系統(tǒng)沒有提供關(guān)于16進(jìn)制色值設(shè)置的方法躬审,大都是自己封裝,封裝方法也大同小異嚼吞,只是完善度的問題盒件,我在Demo中使用的是最簡單的一個沒有任何容錯機(jī)制的宏定義方法:
#define UIColorFromHEX(hexValue, alphaValue) \
[UIColor colorWithRed:((float)((hexValue & 0xFF0000) >> 16))/255.0 \
green:((float)((hexValue & 0x00FF00) >> 8))/255.0 \
blue:((float)(hexValue & 0x0000FF))/255.0 \
alpha:alphaValue]
這種寫法相信十分直觀了,很多不太了解16進(jìn)制色值的轉(zhuǎn)換機(jī)制的朋友舱禽,也可以從上述代碼直觀的去理解炒刁。
3.鍵盤的彈出
鍵盤的彈出是需要考慮的問題,好的progressHUD指示器誊稚,也會考慮鍵盤的彈出翔始,從而自動移動指示器的位置罗心。系統(tǒng)的alert自然不會例外,當(dāng)有鍵盤彈出時城瞎,系統(tǒng)的alert視圖會自動上移渤闷,防止鍵盤的遮蓋。在這里說句題外話脖镀,就是alert動畫中斷系統(tǒng)的鍵盤收起動畫的問題飒箭,這是尤其要注意避免的,一旦在收鍵盤的同時展示alert蜒灰,收鍵盤的動畫就會被強(qiáng)行中斷弦蹂,當(dāng)alert消失時,鍵盤又會詭異的閃現(xiàn)一下……一種解決辦法是强窖,監(jiān)聽鍵盤的收起動畫凸椿,didhide之后再去展示alert。
這里防止鍵盤遮蓋alert的解決辦法也是監(jiān)聽鍵盤的高度去實(shí)現(xiàn)翅溺。
吐槽一句脑漫,有哪位朋友知道如何在鍵盤視圖層添加view嗎?有時候輸入提示想添加在鍵盤上面咙崎,但是一直沒有成功過优幸,鍵盤視圖是在window上,但是卻總也定位不到……
監(jiān)聽鍵盤彈起就很簡單了:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
只是要注意在合適的時機(jī)移除就好叙凡。
鍵盤的高度在監(jiān)聽到的info字典中劈伴,系統(tǒng)的鍵盤信息是比較完善的,但是三方鍵盤就要差很多握爷,甚至有些三方鍵盤的frame是監(jiān)聽不到的……針對這類鍵盤跛璧,除了平時做到放棄使用,還可以根據(jù)經(jīng)驗(yàn)去估計一個值……至于更好的解決辦法就不知道了新啼,可以查閱一些參考資料追城。
// 鍵盤的frame
CGRect keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = keyboardRect.size.height;
有了鍵盤的高度值,就很好處理了燥撞,只需要動態(tài)的去設(shè)置alertView的frame就好了座柱,為了效果自然,也可以添加動畫物舒。具體代碼實(shí)現(xiàn)參考Demo色洞,效果如上圖。
4.Demo中的封裝方法的使用解釋
Demo中的使用情景是填寫圖片驗(yàn)證碼冠胯,需要動態(tài)設(shè)置中間的驗(yàn)證碼獲取按鈕的圖片火诸,還有動態(tài)獲取輸入框的輸入內(nèi)容,這里用的是block荠察,很方便置蜀。
[[JXTAlertView sharedAlertView] showAlertViewWithConfirmAction:^(NSString *inputText) {
NSLog(@"輸入內(nèi)容:%@", inputText);
} andReloadAction:^{
[[JXTAlertView sharedAlertView] refreshVerifyImage:[VerifyNumberView verifyNumberImage]];
}];
方法中的第一個block是點(diǎn)擊確認(rèn)鍵的響應(yīng)奈搜,可以獲取到textField的輸入值,第二個blcok是中間的圖片按鈕的點(diǎn)擊響應(yīng)盯荤,用來設(shè)置按鈕的背景圖馋吗,即從網(wǎng)絡(luò)請求到的驗(yàn)證碼圖片,VerifyNumberView
類即相關(guān)方法秋秤,只是為了模擬效果而搞笑的……可以自行忽略宏粤。
前面提到封裝時用到了單例,這是因?yàn)閎lock交互響應(yīng)和設(shè)置圖片時的需要航缀,可能還有更好的方式吧商架,請指教堰怨。
5.用模態(tài)跳轉(zhuǎn)視圖控制器方式實(shí)現(xiàn)(此方式僅支持iOS8及以后版本)
iOS8之后的API中芥玉,系統(tǒng)的alert增加了UIAlertController
方法,需要使用模態(tài)方式調(diào)用备图。從這點(diǎn)受啟發(fā)灿巧,在自定義時,也嘗試下模態(tài)跳轉(zhuǎn)視圖控制器的方式揽涮。
先看看初始化方法:
- (instancetype)initWithConfirmAction:(ClickBlock)confirmBlock andCancelAction:(CancelBlock)cancelBlcok
{
if (self = [super init]) {
self.confirmBlock = confirmBlock;
self.cancelBlock = cancelBlcok;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
}
return self;
}
其中self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
是設(shè)置跳轉(zhuǎn)的動畫方式抠藕。選擇比較自然的淡入淡出。
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
這一句是核心蒋困,這個樣式可以使得模態(tài)推出的頁面透明化盾似,當(dāng)然還需要在推出的視圖中添加這個這個:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColorFromHEX(0x000000, 0.5);
}
先看一個UIModalPresentationOverFullScreen
這個枚舉值在API中的說明:
UIModalPresentationOverFullScreen NS_ENUM_AVAILABLE_IOS(8_0),
可以看到,這個枚舉樣式雪标,實(shí)在iOS8之后才支持的零院,系統(tǒng)的UIAlertController
也是iOS8之后才有的,從這一點(diǎn)可以簡單猜測系統(tǒng)的alert的實(shí)現(xiàn)機(jī)制村刨。
其他的自定義的方法和上面的大同小異告抄,只是這里不再使用單例,感覺心安了許多……
還要注意一點(diǎn):
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//必須在這里嵌牺,否則動畫無效
[self showAlertView];
}
[self showAlertView]
是創(chuàng)建alert視圖的方法打洼,這一方法,最好是在viewWillAppear
中實(shí)現(xiàn)逆粹,如果直接寫在viewDidLoad
募疮,模態(tài)跳轉(zhuǎn)的動畫會將我們在創(chuàng)建alert時實(shí)現(xiàn)的彈窗動畫中斷掉,也就是彈窗沒有動畫效果僻弹,這不是我們想要的阿浓。viewWillAppear
的執(zhí)行是相對延后的,實(shí)驗(yàn)發(fā)現(xiàn)沒有影響奢方。
根據(jù)這個思路搔扁,也很容易自定義出自己想要的alertView效果了爸舒。
最后還要提一點(diǎn),就是statusBar的樣式稿蹲,如果是UIStatusBarStyleLightContent
扭勉,也就是白色文字,全屏遮罩時的半透明黑背景上的白色文字會顯得很是突兀苛聘,iOS7之后涂炎,系統(tǒng)支持在每個視圖控制器中控制statusBar的樣式(注意navigationBar的影響),這樣设哗,用視圖控制器方式實(shí)現(xiàn)alert的全屏遮罩唱捣,相信可以解決這一問題。
參考文章:
1.視圖彈出后放大又縮小的動畫實(shí)現(xiàn)网梢、類似于alertView效果
2.談?wù)刬OS中粘性動畫以及果凍效果的實(shí)現(xiàn)
3.iOS動畫實(shí)現(xiàn):彈簧效果
4.UITextField 文本字段控件 -- IOS (解決鍵盤遮住View及密文設(shè)定的問題)(實(shí)例)
5.iOS開發(fā)之監(jiān)聽鍵盤高度的變化
6.模態(tài)(modal)畫面的顯示方法