ios - 从语音备忘录应用程序通过 “open in:” 导出音频文件

标签 ios xcode

我有与“保罗”在此处发布的完全相同的问题:Can not export audiofiles via "open in:" from Voice Memos App - 尚未发布有关此主题的答案。

基本上我想做的很简单:
在 iOS 上录制语音备忘录后,我选择“打开方式”并从显示的弹出窗口中选择我的应用程序。

我已经尝试了所有我能想到的方法,并尝试了 LSItemContentTypes,但没有成功。

不幸的是,我没有足够的声誉来评论上面的现有帖子,而且我非常渴望找到解决方案。任何帮助都非常感谢,即使只是知道它是否可行。

谢谢!

最佳答案

在此博客文章 ( http://www.theappguruz.com/blog/share-extension-in-ios-8 ) 中进行了一些实验和大量指导后,似乎可以使用应用扩展(特别是操作扩展)和应用组的组合来实现这一点。我将描述第一部分,它使您能够将录音从语音备忘录获取到您的应用程序扩展程序。第二部分——从应用扩展获取录音到包含的应用(你的“主”应用)——可以使用应用组来完成;请参阅上面的博客文章以了解如何执行此操作。

  • 通过从 Xcode 的菜单中选择 File > New > Target...,在您的项目中为应用程序扩展创建一个新目标。在提示您“为新目标选择模板:”的对话框中,选择“操作扩展”并单击“下一步”。

  • 注意:不要像上面的博客文章示例中那样选择“共享扩展”。这种方法更适合与其他用户共享或发布到网站。
  • 为您的操作扩展填写“产品名称:”,例如 MyActionExtension。此外,对于“操作类型:”,我选择了“演示用户界面”,因为这是 Dropbox 的表现方式。选择此选项将 View Controller ( ActionViewController ) 和 Storyboard ( Maininterface.storyboard ) 添加到您的应用程序扩展。 View Controller 是向用户提供反馈并让用户有机会在将其导出到您的应用程序之前重命名音频文件的好地方。
  • 单击“完成”。系统将提示您“激活“MyActionExtension”方案?”。单击“激活”,这个新方案将被激活。构建它将同时构建操作扩展和包含的应用程序。
  • 单击 Project Navigator (Cmd-0) 中“MyActionExtension”文件夹的显示三角形以显示新创建的 Storyboard、ActionViewController 源文件和 Info.plist。您需要根据需要自定义这些文件。但是现在...
  • 构建并运行您刚刚创建的方案。系统将提示您“选择要运行的应用程序:”。从列表中选择“语音备忘录”,然后单击“运行”。 (为此,您可能需要一个物理设备;我认为模拟器上没有语音备忘录。)这将构建您的操作扩展(及其包含的应用程序)并将其部署到您的设备上。然后继续在您的设备上启动“语音备忘录”。如果您现在使用“语音备忘录”进行录音,然后尝试共享它,您应该会在底行看到您的操作扩展(带有空白图标)。如果您在那里没有看到它,请点击该行中的“更多”按钮,并将您的操作扩展的开关设置为“开”。点击您的操作扩展只会显示一个带有“完成”按钮的空 View 。模板代码寻找一个图像文件,没有找到就没有任何作用。我们将在下一步中解决此问题。
  • 编辑 ActionViewController.swift进行以下更改:

  • 6a.在文件顶部附近添加 AVFoundation 和 AVKit 的导入语句:
    // the next two imports are only necessary because (for our sample code)
    // we have chosen to present and play the audio in our app extension.
    // if all we are going to be doing is handing the audio file off to the
    // containing app (the usual scenario), we won't need these two frameworks
    // in our app extension.
    import AVFoundation
    import AVKit
    

    6b.替换整个 override func viewDidLoad() {...}具有以下内容:
    override func viewDidLoad() {
      super.viewDidLoad()
    
      // Get the item[s] we're handling from the extension context.
    
      // For example, look for an image and place it into an image view.
      // Replace this with something appropriate for the type[s] your extension supports.
    
      print("self.extensionContext!.inputItems = (self.extensionContext!.inputItems)")
    
      var audioFound :Bool = false
      for inputItem: AnyObject in self.extensionContext!.inputItems {
        let extensionItem = inputItem as! NSExtensionItem
        for attachment: AnyObject in extensionItem.attachments! {
          print("attachment = \(attachment)")
          let itemProvider = attachment as! NSItemProvider
          if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeMPEG4Audio as String)
            //|| itemProvider.hasItemConformingToTypeIdentifier(kUTTypeMP3 as String)
            // the audio format(s) we expect to receive and that we can handle
          {
            itemProvider.loadItemForTypeIdentifier(kUTTypeMPEG4Audio as String,
                options: nil, completionHandler: { (audioURL, error) in
              NSOperationQueue.mainQueue().addOperationWithBlock {
    
                if let audioURL = audioURL as? NSURL {
    
                  // in our sample code we just present and play the audio in our app extension
                  let theAVPlayer :AVPlayer = AVPlayer(URL: audioURL)
                  let theAVPlayerViewController :AVPlayerViewController = AVPlayerViewController()
                  theAVPlayerViewController.player = theAVPlayer
                  self.presentViewController(theAVPlayerViewController, animated: true) {
                    theAVPlayerViewController.player!.play()
                  }
    
                }
              }
            })
    
            audioFound = true
            break
          }
        }
    
        if (audioFound) {
          break  // we only handle one audio recording at a time, so stop looking for more
        }
      }
    }
    

    6c。像上一步一样构建并运行。这一次,点击你的 Action 扩展将显示与之前相同的 View Controller ,但现在覆盖了 AVPlayerViewController包含和播放您的录音的实例。还有,两个print()我在代码中插入的语句应该给出如下所示的输出:
    self.extensionContext!.inputItems = [<NSExtensionItem: 0x127d54790> - userInfo: {
        NSExtensionItemAttachmentsKey =     (
            "<NSItemProvider: 0x127d533c0> {types = (\n    \"public.file-url\",\n    \"com.apple.m4a-audio\"\n)}"
        );
    }]
    attachment = <NSItemProvider: 0x127d533c0> {types = (
        "public.file-url",
        "com.apple.m4a-audio"
    )}
    
  • 对操作扩展的 Info.plist 进行以下更改文件:

  • 7a. Bundle display name默认为您为操作扩展指定的任何名称(在本例中为 MyActionExtension)。您可能希望将其更改为 Save to MyApp . (作为比较,Dropbox 使用 Save to Dropbox 。)

    7b.为 key 插入一行 CFBundleIconFile并将其设置为类型 String (第 2 列),并将其值设置为 MyActionIcon或一些这样的。然后您需要提供相应的 5 个图标文件。在我们的示例中,这些将是:MyActionIcon.png , MyActionIcon@2x.png , MyActionIcon@3x.png , MyActionIcon~ipad.png , 和 MyActionIcon@2x~ipad.png . (这些图标对于 iphone 应该是 60x60 点,对于 ipad 应该是 76x76 点。只有 alpha channel 用于确定哪些像素是灰色的,RGB channel 被忽略。)将这些图标文件添加到您的应用程序扩展包,而不是包含应用程序的包.

    7c。在某些时候,您需要设置键 NSExtension > NSExtensionAttributes > NSExtensionActivationRule 的值。除了 TRUEPREDICATE .如果您希望您的操作扩展只为音频文件激活,而不为视频文件、pdf 文件等激活,您可以在此处指定此类谓词。

    以上负责将录音从语音备忘录获取到您的应用程序扩展。以下是如何将录音从应用扩展程序获取到包含应用程序的概述。 (如果时间允许,我稍后会充实它。)这篇博文 ( http://www.theappguruz.com/blog/ios8-app-groups ) 也可能有用。
  • 设置您的应用程序以使用应用程序组。打开 Project Navigator (Cmd-0) 并单击第一行以显示您的项目和目标。为您的应用程序选择目标,单击“功能”选项卡,查找应用程序组功能,并将其开关设置为“开”。添加各种权限后,单击“+”号添加您的应用程序组,并为其命名为 group.com.mycompany.myapp.sharedcontainer . (它必须以 group. 开头,并且可能应该使用某种形式的反向 DNS 命名。)
  • 对您的应用扩展的目标重复上述操作,为其指定与上述相同的名称 ( group.com.mycompany.myapp.sharedcontainer )。
  • 现在您可以从应用程序扩展端将录音的 url 写入应用程序组的共享容器。在 ActionViewController.swift , 替换实例化并呈现 AVPlayerViewController 的代码片段具有以下内容:
    let sharedContainerDefaults = NSUserDefaults.init(suiteName:
        "group.com.mycompany.myapp.sharedcontainer")  // must match the name chosen above
    sharedContainerDefaults?.setURL(audioURL, forKey: "SharedAudioURLKey")
    sharedContainerDefaults?.synchronize()
    
  • 同样,您可以使用以下内容从包含应用程序的一侧读取录音的 url:
    let sharedContainerDefaults = NSUserDefaults.init(suiteName:
        "group.com.mycompany.myapp.sharedcontainer")  // must match the name chosen above
    let audioURL :NSURL? = sharedContainerDefaults?.URLForKey("SharedAudioURLKey")
    
  • 从这里,您可以将音频文件复制到应用程序的沙箱中,例如,应用程序的 Documents 目录或应用程序的 NSTemporaryDiretory() .阅读这篇博文 ( http://www.atomicbird.com/blog/sharing-with-app-extensions ),了解如何使用 NSFileCoordinator 以协调的方式执行此操作。 .

  • 引用:

    Creating an App Extension

    Sharing Data with Your Containing App

    关于ios - 从语音备忘录应用程序通过 “open in:” 导出音频文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36763390/

    相关文章:

    iphone - 如何在横向模式下从底部滑动UIView

    ios - 如何制作空投接收器? [ swift 3]

    ios - 我们可以在 iOS 13 中模拟 NFC 卡吗?

    iphone - 停止 segue 并显示警报

    objective-c - 对按钮xcode视而不见的函数

    ios - 如何在SceneKit中简单地旋转SCNNode?

    ios - 如何使用 NSURLSession 和 NSURLRequest 获取用户提要并在社交网络 iOS 应用程序的 TableView 中更新?

    由于 IBOutletCollection,每当我运行时,XCode 都会抛出 Sigabrt 错误

    ios - 将项目从旧版本的 xcode 转换为新版本

    Xcode:确保标题注释中的文件名正确?