關於自定義 Alert

1、 window add subview

自定義 alert 的時候,沒有作統一規劃,都是實現一個 view,而後添加到 window 上。例如:git

UIView *alert = [[UIView alloc] initWithFrame:];
[window addSubView:alert];
複製代碼

這樣原本也沒有什麼問題,有一天需求來了要支持 Voice Over,問題來了,直接蓋上去的 view 不會自動聚焦,也就是手指左右滑動的時候,不能選中這個 view。並且不能 alert 出來的時候,不能自動讀出。github

2、利用Window 的 makeKeyAndVisible

而後想起之前 UIAlertView 顯示的時候是有一個單獨 window 的,因而也仿照 這樣,本身建一個 window,來顯示 alert,windows

self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.alertWindow.rootViewController = [[UIViewController alloc] init];

    id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
    // Applications that does not load with UIMainStoryboardFile might not have a window property:
    if ([delegate respondsToSelector:@selector(window)]) {
        // we inherit the main window's tintColor self.alertWindow.tintColor = delegate.window.tintColor; } self.alertWindow.hidden = YES; self.frame = self.alertWindow.bounds; // window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard)
    UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
    self.alertWindow.windowLevel = topWindow.windowLevel + 1;

    [self.alertWindow makeKeyAndVisible];
    [self.alertWindow addSubview:self];


    [UIView animateWithDuration:0.25 animations:^{
        self.alertWindow.hidden = NO;
    } completion:nil];

複製代碼

這樣顯示也正常,Voice Over 也能自動聚焦,可是 手指頭不當心點到空白區域的時候,仍是會聚焦到下面的 view,不能只在 alert 上。 並且還有一個嚴重的問題 就是 自定義密碼輸入空間,鍵盤有時候不會自動彈起來,這個之前是沒問題的,不知道跟這個會不會有關係。bash

3、presentViewController

仿照 UIAlertController 的方式,利用 presentViewController來顯示提示框,這個沒有上面的問題,Voice over也能自定聚焦,也不會點到下面的 view。效果還能夠。ide

self.mShowInViewController.definesPresentationContext = YES;
    self.mShowInViewController.providesPresentationContextTransitionStyle = YES;

    if (!self.mShowViewController) {
        UIViewController *vc = [[UIViewController alloc] init];
        vc.modalPresentationStyle = UIModalPresentationOverCurrentContext;
        UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
        backgroundView.backgroundColor = RGBA_COLOR_HEX(0x000000, 0.5);
        vc.backgroundView = backgroundView;
        if (self.alertType == CustomAlertTypeActionSheet) {
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClickBackground)];
            tap.delegate = self;
            [self addGestureRecognizer:tap];
        }
        
        [vc.view addSubview:backgroundView];
        vc.view.backgroundColor = [UIColor clearColor];
//        vc.backgroundView.alpha = 0;
        self.mShowViewController = vc;
    }
}

if (!self.superview) {
        [self.mShowViewController.view addSubview:self];
    }
    self.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
    
    WEAKSELF
    [self.mShowInViewController presentViewController:self.mShowViewController animated:NO completion:^{
        [UIView animateWithDuration:0.25 animations:^{
            weak_self.mShowViewController.view.alpha = 1;
        } completion:^(BOOL finished) {
            if (complete) {
                complete(YES);
            }
        }];
    }];
複製代碼

使用 presentViewController 的時候,由哪一個ViewController來 present 也是個問題,統一處理的話,用跟 UITabBarController 或者是 UINavigationController都是不錯的方案,例如:this

UIViewController * root = [UIApplication sharedApplication].delegate.window.rootViewController;
        if ([root isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tab = (UITabBarController *)root;
            if (tab.tabBar.hidden) {
                //
                UIViewController *selectedVC = tab.selectedViewController;
                self.mShowInViewController = selectedVC;
            } else {
                self.mShowInViewController = root;
            }
        } else {
            self.mShowInViewController = root;
        }

複製代碼

可是這裏有問題,就是容易和其餘的彈窗衝突,形成alert彈不出來,並且若是 alert 不釋放的話,連彈屢次還容易 crashspa

Application tried to present modally an active controllercode

因此呢能夠把 Window 和 present結合起來使用。事件

alertWindow 必定要在 alert 消失的時候給釋放掉get

// method2: 問題是若是self 消失的時候不釋放,那麼 window 會一直存在接收事件,形成 rootWindow 沒法接收事件
        self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.alertWindow.rootViewController = [[UIViewController alloc] init];
        
        id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
        // Applications that does not load with UIMainStoryboardFile might not have a window property:
        if ([delegate respondsToSelector:@selector(window)]) {
            // we inherit the main window's tintColor self.alertWindow.tintColor = delegate.window.tintColor; } // window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard)
        UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
        self.alertWindow.windowLevel = topWindow.windowLevel + 1;
        
        [self.alertWindow makeKeyAndVisible];
        self.mShowInViewController = self.alertWindow.rootViewController;


複製代碼

4、完整代碼

完整代碼在這裏

相關文章
相關標籤/搜索