我正在尝试从 SwiftUI 视图生成一些图像,然后将它们导出到文件系统。我在网上查了一下,似乎无法让事情发挥作用。点击文件导出按钮不会执行任何操作。
我应该将图像转换为其他格式吗?我觉得我错过了一些明显的东西,这比应该的更难。
struct Temp: View {
let screenshots: [ScreenshotModel]
let size: CGSize
@State private var isShowingExport = false
@State private var exportImages = [UIImage]()
var body: some View {
ScrollView {
VStack {
ForEach(exportImages, id: \.self) {
Image(uiImage: $0)
}
Button("Export to Files", systemImage: "folder") {
isShowingExport.toggle()
}
}
}
.task {
await generateImages()
}
.fileExporter(
isPresented: $isShowingExport,
items: exportImages.map { $0.pngData()! }
) { result in
switch result {
case .success:
print("Success")
case .failure(let error):
print(error)
}
} onCancellation: { }
}
func generateImages() async {
exportImages = await withTaskGroup(of: UIImage.self) { group in
for screenshot in screenshots {
group.addTask {
await generateImage(screenshot: screenshot)
}
}
var allImages = [UIImage]()
for await image in group {
allImages.append(image)
}
return allImages
}
}
@MainActor
func generateImage(screenshot: ScreenshotModel) -> UIImage {
let content = ScreenshotPreviewView(
screenshotModel: screenshot,
size: size
).frame(size: size)
let renderer = ImageRenderer(content: content)
return renderer.uiImage!
}
}
您可以使用
.fileExporter(isPresented: ..., documents: ...)
和 ImageDocument: FileDocument
来使用此方法,如图所示。
示例代码:
特别注意
fw.filename
为每个文件指定不同的名称。
struct ContentView: View {
var body: some View {
// for testing
Temp(screenshots: [ScreenshotModel(name: "house"),ScreenshotModel(name: "globe")], size: .zero)
}
}
// for testing
struct ScreenshotModel: Identifiable {
let id = UUID()
var name: String
}
struct Temp: View {
let screenshots: [ScreenshotModel]
let size: CGSize
@State private var isShowingExport = false
@State private var exportImages = [UIImage]()
var body: some View {
ScrollView {
VStack {
ForEach(exportImages, id: \.self) {
Image(uiImage: $0)
}
Button("Export to Files", systemImage: "folder") {
isShowingExport.toggle()
}
}
}
.task {
await generateImages()
}
.fileExporter(
isPresented: $isShowingExport,
documents: exportImages.map{ImageDocument(image: $0)} // <-- here
) { result in
switch result {
case .success:
print("-----> Success")
case .failure(let error):
print(error)
}
} onCancellation: { }
}
func generateImages() async {
exportImages = await withTaskGroup(of: UIImage.self) { group in
for screenshot in screenshots {
group.addTask {
await generateImage(screenshot: screenshot)
}
}
var allImages = [UIImage]()
for await image in group {
allImages.append(image)
}
return allImages
}
}
@MainActor
func generateImage(screenshot: ScreenshotModel) async -> UIImage { // <-- here
// let content = ScreenshotPreviewView(
// screenshotModel: screenshot,
// size: size
// ).frame(size: size)
//
// let renderer = ImageRenderer(content: content)
//
// return renderer.uiImage!
// for testing
return UIImage(systemName: screenshot.name)!
}
}
struct ImageDocument: FileDocument {
static var readableContentTypes: [UTType] { [.png] }
var image: UIImage
init(image: UIImage?) {
self.image = image ?? UIImage()
}
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let image = UIImage(data: data)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.image = image
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
let fw = FileWrapper(regularFileWithContents: image.pngData()!)
fw.filename = String(UUID().uuidString.prefix(6)) // <--- for testing
return fw
}
}