swift Home widget:有没有一种方法可以在不启动父主视图控制器的情况下启动所需的视图控制器?

cngwdvgl  于 2023-11-16  发布在  Swift
关注(0)|答案(1)|浏览(118)

目前,当我点击home小部件时,它将首先启动父主视图控制器,然后只启动我想要的视图控制器(黄色的编辑器视图控制器)

Demo视频


的数据
我想知道,有没有一种方法可以直接启动我想要的视图控制器,而不必启动父主视图控制器?
下面是代码片段。

首页小部件代码

struct sticky_note_widgetEntryView : View {
    var entry: Provider.Entry

    private let widgetURLTemplate = "xxx://widget?objectIDAsString="

    @ViewBuilder
    func contentView(stickyNote: StickyNote, showTitleBar: Bool, showImageAttachments: Bool, widgetWidth: Double) -> some View {
        let url = URL(string: widgetURLTemplate + objectIDAsString)

        let contentView = VStack(alignment: .leading, spacing: 0) {
        }
        .widgetURL(url)

        contentView
    }
}

字符串

场景委托代码

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    static var incomingURL: URL? = nil
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let _ = (scene as? UIWindowScene) else { return }
        
        // https://stackoverflow.com/questions/63697132/detect-app-launch-from-widgetkit-widget-extension
        if let url = connectionOptions.urlContexts.first?.url {
            if SceneDelegate.handleIncomingURL(url) {
                SceneDelegate.incomingURL = nil
            } else {
                SceneDelegate.incomingURL = url
            }
        } else {
            SceneDelegate.incomingURL = nil
        }
    }
    
    // Called on existing scenes
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        if let url = URLContexts.first?.url {
            _ = SceneDelegate.handleIncomingURL(url)
        }
    }
    
    // Return false, if we should retry later on.
    static func handleIncomingURL(_ url: URL) -> Bool {
        if let scheme = url.scheme, scheme == "xxx", let page = url.host {

            var parameters: [String: String] = [:]
            URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.forEach {
                parameters[$0.name] = $0.value
            }

            // Retry required.
            guard let rootViewController = UIWindow.key?.rootViewController else { return false }
              
            // https://stackoverflow.com/questions/33520899/single-function-to-dismiss-all-open-view-controllers
            // Dismiss all previous launched VC except root view controller.
            rootViewController.dismiss(animated: false, completion: nil)
            
            switch page {
            case "widget":
                guard let objectIDAsString = parameters["objectIDAsString"] else { return true }
                guard let objectID = NSManagedObjectID.from(objectIDAsString) else { return true }
                
                if let rootViewController = rootViewController as? UINavigationController {
                    // TODO: rootViewController is parent main view controller.
                    //
                    // We are launching the desired yellow view controller
                    // from parent main view controller.
                    // Is there a way to launch the yellow view controller directly,
                    // without having to show parent main view controller?
                    // 
                    Utils.handleEditNote(rootViewController: rootViewController, objectID: objectID)
                }

            default:
                precondition(false)
            }
        }
        
        return true
    }
}

父主视图控制器代码

class MainViewController: UIViewController {
  
    override func viewDidLoad() {
        super.viewDidLoad()
        
        handleIncomingURLIfPossible()
    }

    private func handleIncomingURLIfPossible() -> Bool {
        if let incomingURL = SceneDelegate.incomingURL {
            SceneDelegate.incomingURL = nil

            SceneDelegate.handleIncomingURL(incomingURL)
            
            return true
        } else {
            return false
        }
    }

im9ewurl

im9ewurl1#

点击小部件打开应用时,应用将始终启动到其初始状态,通常是主视图控制器。您所描述的行为-点击小部件在导航到所需视图之前打开主视图控制器-是iOS应用正常启动过程的一部分。
要从小部件中提供更直接的体验,您可以执行以下操作:
1.**优化启动流程:**当应用通过小部件点击启动时,您可以立即检查URL是否存在,并在不显示主视图控制器的情况下尽快过渡到所需的视图控制器。如果操作正确,这可以是一种无缝体验。
1.**状态恢复:**使用状态恢复来确定应用的最后一个状态,并配置主视图控制器,使其有条件地显示黄色编辑器视图控制器,而不显示动画,使其看起来像是第一个屏幕。
下面是对现有代码结构的修改方法,以实现第一个建议:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

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

        if let url = connectionOptions.urlContexts.first?.url {
            SceneDelegate.handleIncomingURL(url, window: window)
        }
    }

    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        if let url = URLContexts.first?.url {
            SceneDelegate.handleIncomingURL(url, window: window)
        }
    }

    static func handleIncomingURL(_ url: URL, window: UIWindow?) -> Bool {
        // Process the URL and determine if you need to present a specific view controller.
        if let scheme = url.scheme, scheme == "xxx", let page = url.host, page == "widget",
           let parameters = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.reduce(into: [String: String]()) { $0[$1.name] = $1.value },
           let objectIDAsString = parameters["objectIDAsString"], 
           let objectID = NSManagedObjectID.from(objectIDAsString) {
            // Perform your navigation to the yellow editor view controller here.
            // You can pass the objectID or any relevant data to this view controller as needed.
            DispatchQueue.main.async {
                if let viewController = window?.rootViewController as? UINavigationController {
                    // Present or push your editor view controller here
                }
            }
            return true
        }
        return false
    }
}

// Add this extension to UIWindow to find the current top view controller
extension UIWindow {
    static var key: UIWindow? {
        return UIApplication.shared.windows.first { $0.isKeyWindow }
    }

    var topViewController: UIViewController? {
        var top = rootViewController
        while let presented = top?.presentedViewController {
            top = presented
        }
        return top
    }
}

// Then, in your MainViewController's `viewDidLoad` or `viewDidAppear`, you can check if you have any pending URL to handle:
class MainViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        handleIncomingURLIfPossible()
    }
}

字符串
在此代码中,SceneDelegate检查传入的URL并通过尝试直接呈现编辑器视图控制器来处理它。如果您需要在顶部呈现某些内容或关闭任何已经呈现的内容,则UIWindow扩展有助于找到最顶部呈现的视图控制器。
记住在这里替换// Present或push你的编辑器视图控制器,用实际的代码导航到你的编辑器视图控制器,这可能涉及到创建一个带有objectID的视图控制器的新示例,并将其推送到导航堆栈或以模式方式呈现它。
对于状态恢复,您需要在视图控制器中实现必要的协议和方法,以直接恢复到所需的状态。然而,上述方法应该给予您一个从小部件到编辑器视图控制器的更直接的导航路径。

相关问题