You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SceneDelegate中关闭ViewController?iOS离线页面无法消失排查

iOS离线页面联网后无法关闭的问题排查

我正在开发一款基于Swift 5的iOS App,想要实现离线时显示OfflineViewController,用户重新联网后关闭该页面并回到之前的界面。实际情况是离线时该页面能正常弹出,但联网后无法消失,添加刷新按钮尝试关闭也无效。以下是我的代码,请问我哪里出错了?

SceneDelegate 代码

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    let reachability = try! Reachability()


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
        guard let _ = (scene as? UIWindowScene) else { return }

        // Send to homepage if logged in, otherwise login screen.
        let accessToken: String? = KeychainWrapper.standard.string(forKey: "accessToken")
        
        // If access token exists, skip login page
        if accessToken != nil {            
            if let windowScene = scene as? UIWindowScene {
                self.window = UIWindow(windowScene: windowScene)
                let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
                let vc = mainStoryboard.instantiateViewController(withIdentifier: "homeTabController") as! TabBarController
                self.window!.rootViewController = vc
            }
        }

        reachability.whenUnreachable = { [self] _ in
            print("Not reachable (Scene delegate)")

            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            
            let vc = storyboard.instantiateViewController(withIdentifier: "OfflineViewController") as! OfflineViewController
            vc.modalPresentationStyle = .fullScreen

            guard let firstScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
                return
            }

            guard let firstWindow = firstScene.windows.first else {
                return
            }
            
            let rootVC = firstWindow.rootViewController
            rootVC?.dismiss(animated: true, completion: nil)
            rootVC!.present(vc, animated: true, completion: nil)

        }

        do {
            try reachability.startNotifier()
        } catch {
            print("Unable to start notifier")
        }
    }
}

OfflineViewController 代码

import UIKit

class OfflineViewController: UIViewController {
    
    let reachability = try! Reachability()

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            try reachability.startNotifier()
        } catch {
            print("Unable to start notifier")
        }
    }
    
    @IBAction func hitRefresh(_ sender: Any) {
        reachability.whenReachable = { reachability in
                self.dismiss(animated: true, completion: nil)
        }
    }
}

问题分析

  1. 缺失联网状态监听:SceneDelegate只处理了离线事件(whenUnreachable),完全没写联网后的处理逻辑,导致联网时没有触发关闭离线页面的操作。
  2. 重复创建Reachability实例:SceneDelegate和OfflineViewController各建了一个Reachability对象并启动监听,逻辑分散且容易冲突。
  3. 刷新按钮逻辑错误:点击刷新时才设置whenReachable回调,只有网络状态从离线变在线才会触发;如果点击时已经联网,这个回调根本不会执行,自然无法关闭页面。

修复方案

1. 统一在SceneDelegate管理网络监听

修改SceneDelegate,补充联网状态处理,添加离线页面显示状态记录,避免重复弹出:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    let reachability = try! Reachability()
    private var isOfflineVCShowing = false // 标记离线页面是否显示

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
        guard let windowScene = scene as? UIWindowScene else { return }

        // 初始化根视图控制器(补充未登录场景)
        let accessToken: String? = KeychainWrapper.standard.string(forKey: "accessToken")
        let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let rootVC: UIViewController
        
        if accessToken != nil {
            rootVC = mainStoryboard.instantiateViewController(withIdentifier: "homeTabController") as! TabBarController
        } else {
            // 替换为你的登录视图控制器ID
            rootVC = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController")
        }
        
        self.window = UIWindow(windowScene: windowScene)
        self.window!.rootViewController = rootVC
        self.window!.makeKeyAndVisible()

        // 离线状态处理
        reachability.whenUnreachable = { [weak self] _ in
            guard let self = self else { return }
            print("Not reachable (Scene delegate)")
            
            // 避免重复弹出
            guard !self.isOfflineVCShowing else { return }
            
            let offlineVC = mainStoryboard.instantiateViewController(withIdentifier: "OfflineViewController") as! OfflineViewController
            offlineVC.modalPresentationStyle = .fullScreen
            self.window?.rootViewController?.present(offlineVC, animated: true) {
                self.isOfflineVCShowing = true
            }
        }
        
        // 联网状态处理
        reachability.whenReachable = { [weak self] _ in
            guard let self = self else { return }
            print("Reachable (Scene delegate)")
            
            // 关闭离线页面
            guard self.isOfflineVCShowing else { return }
            
            self.window?.rootViewController?.dismiss(animated: true) {
                self.isOfflineVCShowing = false
            }
        }

        do {
            try reachability.startNotifier()
        } catch {
            print("Unable to start notifier")
        }
    }
    
    // 场景销毁时停止监听
    func sceneDidDisconnect(_ scene: UIScene) {
        reachability.stopNotifier()
    }
}

2. 简化OfflineViewController,移除重复监听

删除OfflineViewController中的Reachability实例,刷新按钮直接检查当前网络状态:

import UIKit
import Reachability

class OfflineViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // 移除原有的Reachability监听代码
    }
    
    @IBAction func hitRefresh(_ sender: Any) {
        let reachability = try! Reachability()
        if reachability.connection != .unavailable {
            // 当前已联网,直接关闭页面
            self.dismiss(animated: true, completion: nil)
        } else {
            // 提示用户仍离线
            let alert = UIAlertController(title: "提示", message: "当前仍处于离线状态,请检查网络", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "确定", style: .default))
            self.present(alert, animated: true)
        }
    }
}

额外注意事项

  • 确保已正确导入Reachability框架(CocoaPods或Swift Package Manager引入)。
  • 使用[weak self]避免循环引用,防止内存泄漏。
  • 原代码中未登录场景的根视图控制器初始化缺失,需补充完整。

内容的提问来源于stack exchange,提问作者Two Chains

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 13:01:05