封装iOS系统的弹出对话框

作者: 李贤立 | 来源:发表于2018-01-18 11:31 被阅读274次

    实际开发中,软件和用户的对话非常普遍。对话框如何定义,每个开发人员的思路不尽相同,不过它本身的确就很简单,一个View加上几个按钮就行了,想必有不少程序员是自定义的来做,很方便控制。固然是方便展示大体的风格了,但是往往在细节上处理不好,比如轻微的动画效果和阴影效果,边框的圆角的设置,这些细节没有处理好,很容易影响用户的友好体验。当然也有人直接使用第三方框架了。我在这里介绍的是调用苹果系统的那些弹框,本人很喜欢这种效果。根据实际场景需求,我封装了十一种对话框。如下:

    /**
     *  系统弹框类型
     */
    typedef NS_ENUM(NSInteger, TYZSystemDialogType) {
        /**显示确认和取消*/
        SystemDialogType_Default = 0,
        /**显示确认和取消,着重确认*/
        SystemDialogType_Destructive_Left = 1,
        /**显示确认和取消,着重取消*/
        SystemDialogType_Destructive_Right = 2,
        /**只显示确认*/
        SystemDialogType_OneSure = 3,
        /**只显示确认,着重确认*/
        SystemDialogType_Destructive_Center = 4,
        
        /**显示多行(最多支持6个选项)*/
        SystemDialogType_More = 5,
        
        /**自定义选择项(同默认效果)*/
        SystemDialogType_Custem_Default = 6,
        /**自定义选择项(同1,左边着重)*/
        SystemDialogType_Custem_Destructive_Left = 7,
        /**自定义选择项(同2,右边着重)*/
        SystemDialogType_Custem_Destructive_Right = 8,
        /**自定义选择项(同3,只显示一个)*/
        SystemDialogType_Custem_OneSure = 9,
        /**自定义选择项(同4,一个也着重)*/
        SystemDialogType_Custem_Destructive_Center = 10
    };
    

    首先新建类,需是继承自原生的对话框控制器。
    @interface TYZSystemDialog : UIAlertController
    iOS系统的对话框,本质上是moda出来对话框控制器,所以它必须要的参数就是当前的控制器!如果是在控制器内弹出对话框,一切都好说,但是经过MVC处理后以及深层次的网络请求封装,对话框的弹出很可能不在控制器里,甚至都不能确定到底是哪个控制器界面需要调用。所以一开始需要解决获取当前控制器的事。

    #pragma mark - 获取当前窗口的控制器
    + (UIViewController *)getCurrentViewController {
        UIViewController *ctrl = nil;
        UIApplication *app = [UIApplication sharedApplication];
        if (!ctrl) {
            ctrl = app.keyWindow.rootViewController;
        }
        if (!ctrl) {
            ctrl = [app.windows.firstObject rootViewController];
        }
        if (!ctrl) {
            ctrl = [self viewController];
        }
        if (!ctrl) {
            return nil;
        }
        
        while (!ctrl.view.window && ctrl.presentedViewController) {
            ctrl = ctrl.presentedViewController;
        }
        if (!ctrl.view.window) {
            return nil;
        }
        return ctrl;
    }
    + (UIViewController *)viewController {
        UIViewController *currentViewController = nil;
        UIWindow * window = [[UIApplication sharedApplication] keyWindow];
        if ([window subviews].count == 0) return nil;
        if (window.windowLevel != UIWindowLevelNormal) {
            NSArray *windows = [[UIApplication sharedApplication] windows];
            for (UIWindow * subWindow in windows) {
                if (subWindow.windowLevel == UIWindowLevelNormal) {
                    window = subWindow;
                    break;
                }
            }
        }
        UIView *frontView = [[window subviews] objectAtIndex:0];
        id nextResponder = [frontView nextResponder];
        if ([nextResponder isKindOfClass:[UIViewController class]]) {
            currentViewController = nextResponder;
        } else {
            currentViewController = window.rootViewController;
        }
        return currentViewController;
    }
    

    然后针对十一种对话框在不同需求下进行封装,每种情况分别对应了两个方法,一个是直接传入当前控制器的,一个是通过上述方法自动获取当前控制器的。均采用类方法直接调用,一行代码实现效果,非常方便。

    /**
     *  创建系统弹框
     *  默认悬浮中间,显示确认和取消
     *  @pram title      标题
     *  @pram complement 点击确认的回调
     */
    + (instancetype)dialogWithTitle:(NSString *)title complement:(void(^)(void))complement {
        return [self dialogWithViewController:[self getCurrentViewController] title:title complement:complement];
    }
    + (instancetype)dialogWithViewController:(UIViewController *)viewController title:(NSString *)title complement:(void(^)(void))complement {
        return [self dialogWithViewController:viewController title:title dialogType:SystemDialogType_Default complement:complement];
    }
    /**
     *  创建中间悬浮的系统弹框的类型
     *
     *  @pram title      标题
     *  @pram dialogType 弹框类型(只对前五种有效)
     *  @pram complement 点击确认的回调
     */
    + (instancetype)dialogWithTitle:(NSString *)title dialogType:(TYZSystemDialogType)dialogType complement:(void(^)(void))complement {
        return [self dialogWithViewController:[self getCurrentViewController] title:title dialogType:dialogType complement:complement];
    }
    + (instancetype)dialogWithViewController:(UIViewController *)viewController title:(NSString *)title dialogType:(TYZSystemDialogType)dialogType complement:(void(^)(void))complement {
        TYZSystemDialog *dialog = [TYZSystemDialog alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
        if (dialogType == SystemDialogType_Default) {
            [dialog addAction:[self creatActionWithTitle:[@"确认" S] complement:complement]];
            [dialog addAction:[self creatActionWithTitle:[@"取消" S] complement:nil]];
        } else if (dialogType == SystemDialogType_OneSure) {
            [dialog addAction:[self creatActionWithTitle:[@"确认" S] complement:complement]];
        } else if (dialogType == SystemDialogType_Destructive_Center) {
            [dialog addAction:[self creatActionWithTitle:[@"确认" S] style:UIAlertActionStyleDestructive complement:complement]];
        } else if (dialogType == SystemDialogType_Destructive_Left) {
            [dialog addAction:[self creatActionWithTitle:[@"确认" S] style:UIAlertActionStyleDestructive complement:complement]];
            [dialog addAction:[self creatActionWithTitle:[@"取消" S] complement:nil]];
        } else if (dialogType == SystemDialogType_Destructive_Left) {
            [dialog addAction:[self creatActionWithTitle:[@"确认" S] complement:complement]];
            [dialog addAction:[self creatActionWithTitle:[@"取消" S] style:UIAlertActionStyleDestructive complement:nil]];
        }
        [viewController presentViewController:dialog animated:YES completion:nil];
        return dialog;
    }
    /**
     *  根据传入的数据创建底部弹出的系统弹框的类型 点击选择第几个选项的回调
     *
     *  @pram title      标题
     *  @pram showData   显示的数据,弹框类型只是第六种,从底部弹出
     *  @pram complement 点击确认的回调
     */
    + (instancetype)dialogWithTitle:(NSString *)title showData:(NSArray<NSString *> *)showData complement:(void(^)(NSInteger index))complement {
        return [self dialogWithViewController:[self getCurrentViewController] title:title showData:showData complement:complement];
    }
    + (instancetype)dialogWithViewController:(UIViewController *)viewController title:(NSString *)title showData:(NSArray<NSString *> *)showData complement:(void(^)(NSInteger index))complement {
        TYZSystemDialog *dialog = [TYZSystemDialog alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
        NSInteger number = showData.count > MostSystemDialogNumber ? MostSystemDialogNumber : showData.count;
        for (NSInteger i = 0; i < number; i ++) {
            [dialog addAction:[self creatActionWithTitle:showData[i] complement:^{
                if (complement) {
                    complement(i);
                }
            }]];
        }
        [dialog addAction:[self creatActionWithTitle:[@"取消" S] style:UIAlertActionStyleCancel complement:nil]];
        [viewController presentViewController:dialog animated:YES completion:nil];
        return dialog;
    }
    /**
     *  根据自定义传入的中间弹框选择的数据创建系统弹框的类型 点击选择第几个选项的回调
     *
     *  @pram title      标题
     *  @pram showData   显示的数据
     *  @pram dialogType 弹框类型(只对后五种有效)
     *  @pram complement 点击确认的回调
     */
    + (instancetype)dialogWithTitle:(NSString *)title showData:(NSArray<NSString *> *)showData dialogType:(TYZSystemDialogType)dialogType complement:(void(^)(NSInteger index))complement {
        return [self dialogWithViewController:[self getCurrentViewController] title:title showData:showData dialogType:dialogType complement:complement];
    }
    + (instancetype)dialogWithViewController:(UIViewController *)viewController title:(NSString *)title showData:(NSArray<NSString *> *)showData dialogType:(TYZSystemDialogType)dialogType complement:(void(^)(NSInteger index))complement {
        TYZSystemDialog *dialog = [TYZSystemDialog alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
        if (showData.count == 1) {
            if (dialogType == SystemDialogType_Custem_OneSure) {
                [dialog addAction:[self creatActionWithTitle:showData.firstObject complement:^{
                    if (complement) {
                        complement(0);
                    }
                }]];
            } else if (dialogType == SystemDialogType_Custem_Destructive_Center) {
                [dialog addAction:[self creatActionWithTitle:showData.firstObject style:UIAlertActionStyleDestructive complement:^{
                    if (complement) {
                        complement(0);
                    }
                }]];
            }
        } else if (showData.count == 2) {
            if (dialogType == SystemDialogType_Custem_Default) {
                [dialog addAction:[self creatActionWithTitle:showData.firstObject complement:^{
                    if (complement) {
                        complement(0);
                    }
                }]];
                [dialog addAction:[self creatActionWithTitle:showData.lastObject complement:^{
                    if (complement) {
                        complement(1);
                    }
                }]];
            } else if (dialogType == SystemDialogType_Custem_Destructive_Left) {
                [dialog addAction:[self creatActionWithTitle:showData.firstObject style:UIAlertActionStyleDestructive complement:^{
                    if (complement) {
                        complement(0);
                    }
                }]];
                [dialog addAction:[self creatActionWithTitle:showData.lastObject style:UIAlertActionStyleCancel complement:^{
                    if (complement) {
                        complement(1);
                    }
                }]];
            } else if (dialogType == SystemDialogType_Custem_Destructive_Right) {
                [dialog addAction:[self creatActionWithTitle:showData.firstObject style:UIAlertActionStyleCancel complement:^{
                    if (complement) {
                        complement(0);
                    }
                }]];
                [dialog addAction:[self creatActionWithTitle:showData.lastObject style:UIAlertActionStyleDestructive complement:^{
                    if (complement) {
                        complement(1);
                    }
                }]];
            }
        }
        [viewController presentViewController:dialog animated:YES completion:nil];
        return dialog;
    }
    /**
     *  创建带输入框的非密码形式的弹框
     *
     *  @pram title         标题
     *  @pram placeholder   占位文本
     *  @pram textLength    输入字数上限
     *  @pram complement    点击确认的回调
     */
    + (instancetype)dialogWithTitle:(NSString *)title placeholder:(NSString *)placeholder textLenth:(NSInteger)textLength complement:(void(^)(NSString *text))complement {
        return [self dialogWithViewController:[self getCurrentViewController] title:title placeholder:placeholder textLenth:textLength complement:complement];
    }
    + (instancetype)dialogWithViewController:(UIViewController *)viewController title:(NSString *)title placeholder:(NSString *)placeholder textLenth:(NSInteger)textLength complement:(void(^)(NSString *text))complement {
        return [self dialogWithViewController:viewController title:title placeholder:placeholder textLenth:textLength secureTextEntry:NO complement:complement];
    }
    /**
     *  创建带输入框的弹框
     *
     *  @pram title             标题
     *  @pram placeholder       占位文本
     *  @pram textLength        输入字数上限
     *  @pram secureTextEntry   是否是密码模式
     *  @pram complement        点击确认的回调
     */
    + (instancetype)dialogWithTitle:(NSString *)title placeholder:(NSString *)placeholder textLenth:(NSInteger)textLength secureTextEntry:(BOOL)secureTextEntry complement:(void(^)(NSString *text))complement {
        return [self dialogWithViewController:[self getCurrentViewController] title:title placeholder:placeholder textLenth:textLength secureTextEntry:secureTextEntry complement:complement];
    }
    + (instancetype)dialogWithViewController:(UIViewController *)viewController title:(NSString *)title placeholder:(NSString *)placeholder textLenth:(NSInteger)textLength secureTextEntry:(BOOL)secureTextEntry complement:(void(^)(NSString *text))complement {
        TYZSystemDialog *dialog = [TYZSystemDialog alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
        [dialog addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.placeholder = placeholder;
            textField.secureTextEntry = secureTextEntry;
            textField.clearButtonMode = UITextFieldViewModeWhileEditing;
        }];
        UIAlertAction *action = [UIAlertAction actionWithTitle:[@"确认" S] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            if (complement) {
                complement(dialog.textFields.firstObject.text);
            }
        }];
        [dialog addAction:action];
        [dialog addAction:[self creatActionWithTitle:[@"取消" S] complement:nil]];
        [viewController presentViewController:dialog animated:YES completion:nil];
        return dialog;
    }
    /**
     *  创建默认的按钮
     *
     *  @pram title        按钮标题
     *  @pram complement   点击确认的回调
     */
    + (UIAlertAction *)creatActionWithTitle:(NSString *)title complement:(void(^)(void))complement {
        return [self creatActionWithTitle:title style:UIAlertActionStyleDefault complement:complement];
    }
    /**
     *  创建默认的按钮
     *
     *  @pram title        按钮标题
     *  @pram style        按钮类型
     *  @pram complement   点击确认的回调
     */
    + (UIAlertAction *)creatActionWithTitle:(NSString *)title style:(UIAlertActionStyle)style complement:(void(^)(void))complement {
        UIAlertAction *action = [UIAlertAction actionWithTitle:title style:style handler:^(UIAlertAction * _Nonnull action) {
            if (complement) {
                complement();
            }
        }];
        return action;
    }
    

    注意:如果有重新获取了window,在上面添加了一层,那么这个对话框不会出现在最前面了。我试着将moda出来的对话框控制器的View提到最前面,但是失败。这种矛盾的出现只好规避之。

    相关文章

      网友评论

        本文标题:封装iOS系统的弹出对话框

        本文链接:https://www.haomeiwen.com/subject/ygwioxtx.html