iOS 一种带输入框的自定义alertView的简易封装实现(新增视图控制器实现方式)

前言

相信很多人都会选择自定义alertView,网上也有太多大神封装了类似的三方库,但用来用去,感觉最靠谱的,还是系统的,这也是之前花功夫针对系统的alert进行适配封装的原因之一(iOS (封装)一句话调用系统的alertView和alertController)。
但是系统的效果毕竟是局限的,很多时候,我们仅仅是需要显示一个遮罩层的提示语,又或者是比较麻烦的,需要显示一个可实现多种交互的提示窗,这时,还是得自定义……
下面的封装思路,相对来说简单一些,但肯定不是最好的,甚至因为不得不的原因,用了单例这个东西,如果你有更好的改善方法,还望多多指教。

代码详见GitHub:Demo_JXTAlertView

16.3.8更新Demo,添加了模态跳转视图控制器实现alertView弹出
代码封装度不高,只是提供一个简单的实现思路。

下面是演示效果(定义的样式很简单,因为gif帧数限制,动画效果被削弱了):

1.弹性动画

iOS的动画效果是很强悍的,弹窗展示时,需要一个弹性动画去过渡,弹性动画的实现方式有很多,也比较简单,但难的是自然平滑的效果,下面的两种实现,是参考了网上的例子,两种方法大同小异,代码还是很好理解的,具体参数可自行调整:

  • 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];
}

我采用了第二种方式,相对来说,过渡更为顺滑些,如果想要实现和系统的alert动画相似的效果,可以去掉values数组中的倒数第二项,当然keyTimestimingFunctions也要去掉对应的项。

2.全屏遮罩

一般这类提示窗是显示在一个半透明黑的遮罩层上的,遮罩层的实现也有多种方式,我采用的是在keyWindow层上添加一个和屏幕尺寸相当的半透明黑的view:

_alertBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)];
_alertBackgroundView.backgroundColor = UIColorFromHEX(0x000000, 0.7);
[[UIApplication sharedApplication].keyWindow addSubview:_alertBackgroundView];

有些人习惯设置view的alpha值,但这样做,会导致其子视图也会半透明化,最简单的还是设置背景色的透明度,这里采用的是16进制色值,系统没有提供关于16进制色值设置的方法,大都是自己封装,封装方法也大同小异,只是完善度的问题,我在Demo中使用的是最简单的一个没有任何容错机制的宏定义方法:

#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进制色值的转换机制的朋友,也可以从上述代码直观的去理解。

3.键盘的弹出

键盘的弹出是需要考虑的问题,好的progressHUD指示器,也会考虑键盘的弹出,从而自动移动指示器的位置。系统的alert自然不会例外,当有键盘弹出时,系统的alert视图会自动上移,防止键盘的遮盖。在这里说句题外话,就是alert动画中断系统的键盘收起动画的问题,这是尤其要注意避免的,一旦在收键盘的同时展示alert,收键盘的动画就会被强行中断,当alert消失时,键盘又会诡异的闪现一下……一种解决办法是,监听键盘的收起动画,didhide之后再去展示alert。
这里防止键盘遮盖alert的解决办法也是监听键盘的高度去实现。

吐槽一句,有哪位朋友知道如何在键盘视图层添加view吗?有时候输入提示想添加在键盘上面,但是一直没有成功过,键盘视图是在window上,但是却总也定位不到……

监听键盘弹起就很简单了:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow:)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

只是要注意在合适的时机移除就好。
键盘的高度在监听到的info字典中,系统的键盘信息是比较完善的,但是三方键盘就要差很多,甚至有些三方键盘的frame是监听不到的……针对这类键盘,除了平时做到放弃使用,还可以根据经验去估计一个值……至于更好的解决办法就不知道了,可以查阅一些参考资料。

// 键盘的frame
CGRect keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = keyboardRect.size.height;

有了键盘的高度值,就很好处理了,只需要动态的去设置alertView的frame就好了,为了效果自然,也可以添加动画。具体代码实现参考Demo,效果如上图。

4.Demo中的封装方法的使用解释

Demo中的使用情景是填写图片验证码,需要动态设置中间的验证码获取按钮的图片,还有动态获取输入框的输入内容,这里用的是block,很方便。

[[JXTAlertView sharedAlertView] showAlertViewWithConfirmAction:^(NSString *inputText) {
    NSLog(@"输入内容:%@", inputText);
} andReloadAction:^{
    [[JXTAlertView sharedAlertView] refreshVerifyImage:[VerifyNumberView verifyNumberImage]];
}];

方法中的第一个block是点击确认键的响应,可以获取到textField的输入值,第二个blcok是中间的图片按钮的点击响应,用来设置按钮的背景图,即从网络请求到的验证码图片,VerifyNumberView类即相关方法,只是为了模拟效果而搞笑的……可以自行忽略。

前面提到封装时用到了单例,这是因为block交互响应和设置图片时的需要,可能还有更好的方式吧,请指教。

5.用模态跳转视图控制器方式实现(此方式仅支持iOS8及以后版本)

iOS8之后的API中,系统的alert增加了UIAlertController方法,需要使用模态方式调用。从这点受启发,在自定义时,也尝试下模态跳转视图控制器的方式。
先看看初始化方法:

- (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;是设置跳转的动画方式。选择比较自然的淡入淡出。
self.modalPresentationStyle = UIModalPresentationOverFullScreen;这一句是核心,这个样式可以使得模态推出的页面透明化,当然还需要在推出的视图中添加这个这个:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColorFromHEX(0x000000, 0.5);
}

先看一个UIModalPresentationOverFullScreen这个枚举值在API中的说明:

UIModalPresentationOverFullScreen NS_ENUM_AVAILABLE_IOS(8_0),

可以看到,这个枚举样式,实在iOS8之后才支持的,系统的UIAlertController也是iOS8之后才有的,从这一点可以简单猜测系统的alert的实现机制。
其他的自定义的方法和上面的大同小异,只是这里不再使用单例,感觉心安了许多……
还要注意一点:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    //必须在这里,否则动画无效
    [self showAlertView];
}

[self showAlertView]是创建alert视图的方法,这一方法,最好是在viewWillAppear中实现,如果直接写在viewDidLoad,模态跳转的动画会将我们在创建alert时实现的弹窗动画中断掉,也就是弹窗没有动画效果,这不是我们想要的。viewWillAppear的执行是相对延后的,实验发现没有影响。
根据这个思路,也很容易自定义出自己想要的alertView效果了。

最后还要提一点,就是statusBar的样式,如果是UIStatusBarStyleLightContent,也就是白色文字,全屏遮罩时的半透明黑背景上的白色文字会显得很是突兀,iOS7之后,系统支持在每个视图控制器中控制statusBar的样式(注意navigationBar的影响),这样,用视图控制器方式实现alert的全屏遮罩,相信可以解决这一问题。

参考文章:
1.视图弹出后放大又缩小的动画实现、类似于alertView效果
2.谈谈iOS中粘性动画以及果冻效果的实现
3.iOS动画实现:弹簧效果
4.UITextField 文本字段控件 -- IOS (解决键盘遮住View及密文設定的问题)(实例)
5.iOS开发之监听键盘高度的变化
6.模态(modal)画面的显示方法

·转载请声明出处·

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容