封装iOS系统的弹出对话框

实际开发中,软件和用户的对话非常普遍。对话框如何定义,每个开发人员的思路不尽相同,不过它本身的确就很简单,一个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提到最前面,但是失败。这种矛盾的出现只好规避之。

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