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

前言

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

代码详见GitHub:Demo_JXTAlertView

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

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

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

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)画面的显示方法

·转载请声明出处·

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