一、 window add subview
自定義 alert 的時候,沒有做統一規劃,都是實現一個 view,然後新增到 window 上。例如:
UIView *alert = [[UIView alloc] initWithFrame:];
[window addSubView:alert];
複製程式碼
這樣本來也沒有什麼問題,有一天需求來了要支援 Voice Over
,問題來了,直接蓋上去的 view
不會自動聚焦,也就是手指左右滑動的時候,不能選中這個 view
。而且不能 alert
出來的時候,不能自動讀出。
二、利用Window 的 makeKeyAndVisible
然後想起以前 UIAlertView 顯示的時候是有一個單獨 window 的,於是也仿照 這樣,自己建一個 window,來顯示 alert,
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 上。
而且還有一個嚴重的問題 就是 自定義密碼輸入空間,鍵盤有時候不會自動彈起來,這個以前是沒問題的,不知道跟這個會不會有關係。
三、presentViewController
仿照 UIAlertController 的方式,利用 presentViewController
來顯示提示框,這個沒有上面的問題,Voice over
也能自定聚焦,也不會點到下面的 view。效果還可以。
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
都是不錯的方案,例如:
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 不釋放的話,連彈多次還容易 crash
Application tried to present modally an active controller
所以呢可以把 Window 和 present結合起來使用。
alertWindow 一定要在 alert 消失的時候給釋放掉
// 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;
複製程式碼
四、完整程式碼
完整程式碼在這裡