从今日扩展程序(小部件)在主机应用程序中执行操作,无需打开应用程序ios

时间:2016-12-28 15:44:28

标签: ios swift swift3 today-extension

我想在今天的扩展程序(Widget)中管理包含应用程序的操作。

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

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

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

代码:

主要包含应用代码

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()
}
用户更新默认值时未触发

通知。它是应用重启时的更新值。

那么如何解决这个问题?

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

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

2 个答案:

答案 0 :(得分:9)

使用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的文档类型部分,在网址下声明foo://(或您使用的任何其他内容),然后在AppDelegate中实施application(_:open:options:),以便在网址传递时应用开始播放音乐是foo://startPlaying

how to add URL to Xcode

答案 1 :(得分:4)

  1. 创建自定义网址Scehme

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

  3. 每当您点击按钮时,将从 Appdelegate UIApplication 代表

    调用主机应用
    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. 从那里发出通知,然后在你的hostapp的任何地方观察。

    小工具按钮操作代码

    @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. 查看自定义网址类型: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Inter-AppCommunication/Inter-AppCommunication.html

  6. 注意:

      

    应用扩展程序与其之间没有直接通信   包含app;通常,包含应用程序甚至不运行   包含的扩展名正在运行。包含应用的应用扩展程序   并且主机应用程序根本不进行通信。

         

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

         

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

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