从今天的扩展(小部件)在主机应用程序中执行操作,无需打开应用程序ios [英] Perform action in host app from Today extension(Widget) Without opening app ios

查看:233
本文介绍了从今天的扩展(小部件)在主机应用程序中执行操作,无需打开应用程序ios的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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



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



代码:



主要包含应用代码

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

让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 letPlaying = sharedDefaults?.bool(forKey:isPlaing){

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

} else {

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

sharedDefaults?.synchronize()
}

当a时没有触发通知用户更新默认值。应用程序重新启动时它是更新后的值。



那么如何解决这个问题?



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



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

解决方案

使用


I want to manage some action in containing app from today extension(Widget).

Full description: in my containing app, some action (like play/pause audio) perform. And want to manage that action also from today extension(widget). An action continues to perform in background state as well.

In today extension, the same action will perform. so for that, if in the main containing app already starts an action and send it into background state, a user can pause action from a widget. and the user also can start/pause action any time from the widget (today extension).

For achieve this goal I used UserDefault with app Group capability and store one boolean value. when widget present it checks boolean value and set button state play/pause. it's set correctly but when I press extension button action does not perform in host app.

code:

in main containing app code

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
  }

In Extension Class:

@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()
}

notification was not fired when a user updates default. it's updated value when the app restarts.

so how to solve this issue?

and the same thing wants to do in opposite means from containing app to a widget. (easy to user single action object but how?)

And is any other way to perform a quick action in containing app from extension without opening App?

解决方案

Use MMWormhole (or its new and unofficial Swift version, just Wormhole). It's very simple.

In the app's view controller:

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
    }
}

In the extension:

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")
}

Declare foo:// (or whatever else you use) in Xcode's Document Types section, under URLs, then implement application(_:open:options:) in your AppDelegate so that the app starts playing music when the URL passed is foo://startPlaying.

这篇关于从今天的扩展(小部件)在主机应用程序中执行操作,无需打开应用程序ios的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆