SwiftUI 和 ShareLink - 共享表上的“保存”按钮会使应用程序崩溃

问题描述 投票:0回答:1

我有一个 iOS/iPadOS 应用程序。当我在 Mac 上将应用程序作为“为 iPad 设计”应用程序运行时,共享表上的“保存”按钮会使应用程序崩溃。

我想与 ImageRenderer 共享视图,但这里有一个更简单的示例,我尝试将 SF 符号作为图像共享。

ShareLink(item: Image(systemName: "swift"), preview: SharePreview(Text("PNG"), image: Image(systemName: "swift")))

共享按钮有效。它打开共享表。 “添加到照片”有效。 “复制”有效。按“复制”后,我可以将图像文件粘贴到 Finder 中。但是当我按下“保存”按钮时,我的应用程序崩溃并出现错误:

Thread 1: "-[NSItemProvider pathExtension]: unrecognized selector sent to instance 0x600001c1e290"

如何修复这个错误?如何使保存图像起作用?也许我需要 plist 中的额外权限?我已经有“隐私 - 照片库添加使用说明”并且 iOS 和 iPadOS 上的所有内容都可以正常工作。

macos swiftui apple-silicon swiftui-sharelink sharelink
1个回答
0
投票

Mac 上的 SwiftUI ShareLink“保存”按钮崩溃

问题

在 Mac 上将 iOS/iPadOS 应用程序作为“Designed for iPad”应用程序运行时,使用

ShareLink
共享图像会导致在按下共享表上的“保存”按钮时应用程序崩溃。 “添加到照片”和“复制”等其他操作可以正常工作。发生崩溃并出现以下错误:

Thread 1: "-[NSItemProvider pathExtension]: unrecognized selector sent to instance 0x600001c1e290"

示例代码:

ShareLink(item: Image(systemName: "swift"), preview: SharePreview(Text("PNG"), image: Image(systemName: "swift")))

原因

此问题可能是由于 macOS 处理文件操作的方式与 iOS/iPadOS 不同所致。在 macOS 上运行时,所使用的

NSItemProvider
似乎没有预期的属性或方法。

解决方案

以下是解决此问题的几种方法:

1.使用 UIImage 代替 Image

在分享之前将 SwiftUI

Image
转换为
UIImage

import SwiftUI

struct ContentView: View {
    var body: some View {
        if let uiImage = UIImage(systemName: "swift") {
            ShareLink(item: uiImage, preview: SharePreview("Swift Symbol", image: Image(uiImage: uiImage)))
        }
    }
}

2.创建自定义 NSItemProvider

创建一个可处理 iOS 和 macOS 的自定义

NSItemProvider

import SwiftUI
import UniformTypeIdentifiers

struct ContentView: View {
    var body: some View {
        ShareLink(item: CustomItemProvider(image: UIImage(systemName: "swift")!),
                  preview: SharePreview("Swift Symbol", image: Image(systemName: "swift")))
    }
}

class CustomItemProvider: NSItemProvider {
    init(image: UIImage) {
        super.init()
        
        self.registerDataRepresentation(forTypeIdentifier: UTType.png.identifier, visibility: .all) { completion in
            guard let data = image.pngData() else {
                completion(nil, NSError(domain: "ImageError", code: 0, userInfo: nil))
                return nil
            }
            completion(data, nil)
            return nil
        }
    }
}

3.使用临时文件

将图像保存到临时文件并共享:

import SwiftUI

struct ContentView: View {
    var body: some View {
        if let url = saveImageToTemporaryFile() {
            ShareLink(item: url, preview: SharePreview("Swift Symbol", image: Image(systemName: "swift")))
        }
    }
    
    func saveImageToTemporaryFile() -> URL? {
        guard let image = UIImage(systemName: "swift"),
              let data = image.pngData() else { return nil }
        
        let temporaryDirectoryURL = FileManager.default.temporaryDirectory
        let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent("swiftSymbol.png")
        
        do {
            try data.write(to: temporaryFileURL)
            return temporaryFileURL
        } catch {
            print("Error saving file: \(error)")
            return nil
        }
    }
}

4.将所需的功能添加到您的应用程序的权利中

确保您的应用程序具有在 macOS 上访问文件所需的权限:

  1. 在 Xcode 中,转到目标的“签名和功能”选项卡。
  2. 添加“应用沙箱”功能。
  3. 在“应用程序沙箱”下,为“读/写”启用“用户选择的文件”。

5.单独处理 macOS

检测应用程序是否在 macOS 上运行并提供不同的共享机制:

import SwiftUI

struct ContentView: View {
    var body: some View {
        if #available(macOS 11.0, *) {
            // macOS-specific sharing
            Button("Share") {
                let picker = NSSavePanel()
                picker.allowedContentTypes = [.png]
                picker.canCreateDirectories = true
                picker.isExtensionHidden = false
                picker.title = "Save Image"
                picker.message = "Choose a location to save the image"
                picker.nameFieldStringValue = "swiftSymbol.png"

                picker.begin { result in
                    if result == .OK {
                        if let url = picker.url,
                           let image = NSImage(systemSymbolName: "swift", accessibilityDescription: nil),
                           let tiffData = image.tiffRepresentation,
                           let bitmapImage = NSBitmapImageRep(data: tiffData),
                           let pngData = bitmapImage.representation(using: .png, properties: [:]) {
                            do {
                                try pngData.write(to: url)
                            } catch {
                                print("Error saving file: \(error)")
                            }
                        }
                    }
                }
            }
        } else {
            // iOS/iPadOS sharing
            ShareLink(item: Image(systemName: "swift"), preview: SharePreview("Swift Symbol", image: Image(systemName: "swift")))
        }
    }
}

结论

该问题源于 macOS 和 iOS/iPadOS 处理文件操作和共享的方式不同。通过使用上述方法之一,您应该能够解决崩溃问题并在两个平台上成功共享图像。

请记住在 iOS/iPadOS 设备和 Mac 上进行彻底测试,以确保该解决方案适用于所有平台。

© www.soinside.com 2019 - 2024. All rights reserved.