【问题标题】:Perform action in host app from Today extension(Widget) Without opening app ios从今天扩展(小部件)在主机应用程序中执行操作而不打开应用程序 ios
【发布时间】:2016-12-28 15:44:28
【问题描述】:

我想管理一些包含今日扩展(小部件)中的应用程序的操作。

完整说明: 在我的包含应用程序中,执行了一些操作(如播放/暂停音频)。并希望从今天的扩展(小部件)中管理该操作。一个动作也会在后台状态下继续执行。

在今天的扩展中,将执行相同的操作。因此,如果在主包含的应用程序中已经启动了一个动作并将其发送到后台状态,用户可以暂停一个小部件的动作。并且用户还可以随时从小部件开始/暂停操作(今天的扩展)。

为了实现这个目标,我使用了带有应用组功能的 UserDefault 并存储了一个布尔值。当小部件出现时,它会检查布尔值并设置按钮状态播放/暂停。它设置正确,但是当我按下扩展按钮时,主机应用程序中没有执行操作。

代码:

在包含应用程序代码的主目录中

override func viewDidLoad() {
    super.viewDidLoad()
    let objUserDefault = UserDefaults(suiteName:"group.test.TodayExtensionSharingDefaults")

    let objTemp = objUserDefault?.object(forKey: "value")

    self.btnValue.isSelected = objTemp

    NotificationCenter.default.addObserver(self, selector: #selector(self.userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
}




func userDefaultsDidChange(_ notification: Notification) {

        let objUserDefault = UserDefaults(suiteName: "group.test.TodayExtensionSharingDefaults")
        objUserDefault?.synchronize()   
        let objTemp = objUserDefault?.object(forKey: "value")
        self.btnValue.isSelected = objTemp
  }

在扩展类中:

@IBAction func onPlayPause(_ sender: UIButton) {
       DispatchQueue.main.async {
       let sharedDefaults = UserDefaults(suiteName: "group.test.TodayExtensionSharingDefaults")

       if let isPlaying = sharedDefaults?.bool(forKey: "isPlaing") {

       sharedDefaults?.set(!isPlaying, forKey: "isPlaying")

       }else{

        sharedDefaults?.set(false, forKey: "isPlaying")
        }

        sharedDefaults?.synchronize()
}

用户更新默认值时未触发通知。当应用重新启动时,它是更新的值。

那么如何解决这个问题呢?

同样的事情想要以相反的方式从包含应用程序到小部件。 (易于用户使用单个操作对象,但如何操作?)

还有其他方法可以在不打开应用程序的情况下从扩展程序中包含应用程序执行快速操作吗?

【问题讨论】:

  • 你在哪里触发你的通知?

标签: ios swift swift3 today-extension


【解决方案1】:

使用MMWormhole(或其新的非官方Swift 版本,只需Wormhole)。很简单。

在应用的视图控制器中:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let wormhole = MMWormhole(applicationGroupIdentifier: "group.test.TodayExtensionSharingDefaults",
                              optionalDirectory: "TodayExtensionSharingDefaults")

    wormhole.listenForMessage(withIdentifier: "togglePlayPause") { [weak self] _ in
        guard let controller = self else { return }
        controller.btnValue.isSelected = controller.btnValue.isSelected
    }
}

在扩展中:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view from its nib.

    self.wormhole = MMWormhole(applicationGroupIdentifier: "group.test.TodayExtensionSharingDefaults", optionalDirectory: "TodayExtensionSharingDefaults")
}

@IBAction func onPlayPause(_ sender: UIButton) {
    guard let wormhole = self.wormhole else { extensionContext?.openURL(NSURL(string: "foo://startPlaying")!, completionHandler: nil) } // Throw error here instead of return, since somehow this function was called before viewDidLoad (or something else went horribly wrong)

    wormhole.passMessageObject(nil, identifier: "togglePlayPause")
}

在 Xcode 的 Document Types 部分的 URL 下声明 foo://(或您使用的任何其他内容),然后在您的 AppDelegate 中实现 application(_:open:options:),以便应用程序在传递的 URL 为 foo://startPlaying 时开始播放音乐。

【讨论】:

  • 嗨,当应用程序处于非活动状态和后台时,此库不起作用
  • 如果应用程序没有运行,音乐将如何开始?
  • 这就是问题所在。想要开始和停止 playAndRecord
  • 库 (Swift) 已经 3 年没有更新了。也许没有。
【解决方案2】:
  1. 创建自定义URL方案

  2. 检查组数据。(您是否设置正确)

  3. 当您点击按钮时,主机应用程序将被 AppdelegateUIApplication 委托

    调用
    func application(_ application: UIApplication, open urls: URL, sourceApplication: String?, annotation: Any) -> Bool {
    
            let obj = urls.absoluteString.components(separatedBy: "://")[1]
            NotificationCenter.default.post(name: widgetNotificationName, object: obj)
            print("App delegate")
            return true
        }
    
  4. 从那里触发你的通知,然后在你的主机应用程序的任何地方观察它。

    小部件按钮操作代码

    @IBAction func doActionMethod(_ sender: AnyObject) {
    
        let button = (sender as! UIButton)
        var dailyThanthi = ""
        switch button.tag {
        case 0:
            dailyThanthi = "DailyThanthi://h"
        case 1:
            dailyThanthi = "DailyThanthi://c"
        case 2:
            dailyThanthi = "DailyThanthi://j"
            //        case 3:
            //            dailyThanthi = "DailyThanthi://s"
            //        case 4:
            //            dailyThanthi = "DailyThanthi://s"
        default:
            break
        }
    
        let pjURL = NSURL(string: dailyThanthi)!
        self.extensionContext!.open(pjURL as URL, completionHandler: nil)
    
    }
    
  5. 查看自定义 url 类型: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Inter-AppCommunication/Inter-AppCommunication.html

  6. 注意:

    应用扩展程序和它的应用程序扩展程序之间没有直接通信 包含应用程序;通常,包含的应用程序甚至没有运行,而 包含的扩展正在运行。应用扩展包含的应用 和宿主应用程序根本不通信。

    在典型的请求/响应事务中,系统代表宿主应用打开应用扩展,在扩展中传送数据 主机提供的上下文。扩展显示用户界面, 执行一些工作,并且,如果适合扩展的目的, 向主机返回数据。

    图 2-2 中的虚线表示应用扩展程序与其包含的应用程序之间可用的有限交互。今天 小部件(并且没有其他应用程序扩展类型)可以要求系统打开 通过调用openURL:completionHandler: 方法来包含它的应用程序 NSExtensionContext 类。如中的读/写箭头所示 图 2-3,任何应用扩展及其包含的应用都可以访问共享 私有定义的共享容器中的数据。完整的词汇 扩展、其宿主应用程序及其包含之间的通信 app 的简单形式如图 2-3 所示。

    https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html

【讨论】:

  • 没有得到第 3 点和第 4 点。你能解释得更详细些吗?
  • 这将打开您的应用程序,检查 6 点..但您说的是不打开应用程序,我认为这是不可能的,请检查文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2019-10-19
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多