一. 布景
由于产品需求,期望一些弹框,只显现在主页,盖住整个屏幕,当然包括tabbar,点击跳转其他页面之后,弹框被盖住,回来来仍然弹框仍然显现。
二. 分析
咱们分析这个需求的难点在于,假如弹框正常显现在当时UIApplication.shared.keyWindow或者UITabbarController的View上的话,弹框的层级是高于当时VC地点UINavigationController,因而点击跳转到其他页面之后,当时弹框仍然会盖住跳转之后的VC。
三. 实现
以上的问题,有如下几种处理办法,咱们具体比照一下。
A. 计划一
将弹框加在UIApplication.shared.keyWindow上,当push到其他页面时候,将弹框隐藏,回来当时页面之后再显现。
这里会存在一个问题便是苹果支撑侧滑回来,所以显现和隐藏逻辑只能放在viewDidAppear和viewDidDisappear。这样就会形成显现的时候,忽然闪现出来的问题。
B. 计划二
使用present半屏弹框款式,半屏弹框款式。这个办法明显也不可。
-
present半屏弹框款式,跟弹框盖住全屏不一致,且后边的VC也会缩小。 -
present半屏弹框款式,点击跳转其他VC也是半屏的。
C. 计划三
- 将
tabbarViewController作为UINavigationController的rootViewController,然后作为window的rootViewController
if let window = UIApplication.shared.delegate?.window {
let naviVC = UINavigationController.init(rootViewController: self.getTabBarController())
window?.rootViewController = naviVC
window?.makeKeyAndVisible()
}
然后弹框显现在UITabbarController的View,点击弹框跳转的时候获取window?.rootViewController及UINavigationController去进行相关push操作,这样跳转的页面的就能盖住弹框。由于跳转后的VC和UITabbarController是都是在同一个UINavigationController栈里边,后边的VC自然能够盖住前面的VC。
尽管这样能够有效的处理当时问题,但这里就改变了项目结构,导致许多当地的判别需求修改,比如判别当时页面是否为主页、还有路由框架,由于曾经都是从UITabbarController去获取到对应的selectedViewController,即对应的UINavigationController去跳转,也便是UITabbarController有多个UINavigationController去办理对应的视图。
但现在由于是获取到了window?.rootViewController的UINavigationController去push,也便是UITabbarController的UINavigationController,就没使用到。
改动和测验规模就相比照较大。
D. 计划四
-
创建一个
Alert等级的UIWindow即dialogWindow, 显现等级高于UIApplication.shared.delegate.window -
然后设置该
UIWindow的rootViewController为UINavigationController -
UINavigationController的rootViewController为自定义的FJFDialogViewController -
FJFDialogViewController里边重写了loadView办法,生成自定义的FJFDialogView赋值给self.view。 -
FJFDialogViewController里边也重写了viewDidAppear和viewDidDisappear,在这两个办法里边别离取判别,当时AlertWindow是否应该显现。假如当时FJFDialogView的subviews数量为0,而且UINavigationController的viewControllers也为1,则将AlertWindow隐藏 -
假如当时
FJFDialogView的subviews数量大于0,或者UINavigationController的子viewControllers也为大于1,则将AlertWindow显现。 -
最终在FJFDialogView里边从头addSubview和willRemoveSubview办法,当调用addSubview增加子视图时,去判别当时dialogWindow是否应该显现,当调用willRemoveSubview,即表示子视图从dialogWindow移除时,去判别当时dialogWindow是否应该隐藏。
具体代码如下:
import UIKit
public class FJFDialogView: UIView {
// MARK: - Override
public override func addSubview(_ view: UIView) {
super.addSubview(view)
FJFDialogWindowManager.checkIsShowAlertWindow()
}
public override func willRemoveSubview(_ subview: UIView) {
super.willRemoveSubview(subview)
/// 由于willRemove还没有真正移除,所以加点延迟等真正移除后去判别
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
FJFDialogWindowManager.checkIsShowAlertWindow()
}
}
}
public class FJFDialogViewController: UIViewController {
// MARK: - Life
public override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
FJFDialogWindowManager.checkIsShowAlertWindow()
}
public override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
FJFDialogWindowManager.checkIsShowAlertWindow()
}
// MARK: - Override
public override func loadView() {
self.view = FJFDialogView(frame: UIScreen.main.bounds)
}
}
public class FJFDialogWindowManager {
// alter弹框显现 window
public static var dialogWindow: UIWindow?
// 获取 弹框 显现window
public class func getAlertShowWindow() -> UIWindow {
if let tmpWindow = self.dialogWindow {
return tmpWindow
}
var alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.windowLevel = .alert
var navigationVc = UINavigationController.init(rootViewController: FJFDialogViewController.init())
navigationVc.view.backgroundColor = .clear
navigationVc.navigationBar.isHidden = true
alertWindow.rootViewController = navigationVc
alertWindow.makeKeyAndVisible()
alertWindow.isHidden = true
self.dialogWindow = alertWindow
return alertWindow
}
/// 隐藏 alert的window
public class func hiddenAlertWindow() {
self.dialogWindow?.isHidden = true
}
/// 校验是否 显现alert的window
public class func checkIsShowAlertWindow() {
let subViewCount = self.getAlertWindowView()?.subviews.count ?? 0
let subVcCount = self.getAlertWindowNavigationCount()
if subViewCount == 0, subVcCount == 1 {
self.dialogWindow?.isHidden = true
}
if subViewCount > 0 ||
subVcCount > 1 {
self.dialogWindow?.isHidden = false
}
}
/// 获取 弹框 window 显现view
public class func getAlertWindowView() -> UIView? {
if let rootVc = self.getAlertShowWindow().rootViewController,
let nav = rootVc as? UINavigationController,
let vc = nav.viewControllers.first {
return vc.view
}
return nil
}
/// 获取 弹框 window 显现view
public class func getAlertWindowNavigationCount() -> Int {
if let rootVc = self.getAlertShowWindow().rootViewController,
let nav = rootVc as? UINavigationController {
return nav.viewControllers.count
}
return 0
}
public class func destoryAlertShowWindow() {
self.dialogWindow?.rootViewController = nil
self.dialogWindow?.removeFromSuperview()
self.dialogWindow = nil
}
}
然后将路由里边改造下,增加参数是否来自于isAlertWindow,默认值为false。假如当时跳转来自alertWindow的弹框,就从alertWindow去获取跳转的UINavigationController.
// MARK: - 获取活跃VC
public class func getActivityViewController(_ isAlertWindow: Bool = false) -> UIViewController? {
var viewController: UIViewController?
let windows: [UIWindow] = UIApplication.shared.windows
var activeWindow: UIWindow?
for window in windows {
if isAlertWindow {
if window.windowLevel == UIWindow.Level.alert {
activeWindow = window
break
}
} else {
if window.windowLevel == UIWindow.Level.normal {
activeWindow = window
break
}
}
}
if activeWindow != nil && activeWindow!.subviews.count > 0 {
let frontView: UIView = activeWindow!.subviews.last!
var nextResponder: UIResponder? = frontView.next
while nextResponder!.isKind(of: UIWindow.self) == false {
if nextResponder!.isKind(of: UIViewController.self) {
viewController = nextResponder as! UIViewController?
break
} else {
nextResponder = nextResponder!.next
}
}
if nextResponder!.isKind(of: UIViewController.self) {
viewController = nextResponder as! UIViewController?
} else {
viewController = activeWindow!.rootViewController
}
while viewController!.presentedViewController != nil {
viewController = viewController!.presentedViewController
}
}
return viewController
}
- 这样改动只针对这种类型的弹框,规模相对可控,测验进行测验也只需求重视这几个弹框。



