首页 > 解决方案 > 错误:从 PHCachingImageManager 调用 requestImage 时 IOSurfaceCreate 'RGBA' 失败

问题描述

我正在使用 PHCachingImageManager 获取 iOS 照片库中所有图像的缩略图图像,然后使用 SwiftUI 列表进行渲染。它似乎适用于少量图像(例如,模拟器加载的六个图像),但考虑到 1000 张图像,我多次出现此错误。

IIO_CreateIOSurfaceWithFormatAndBuffer:594: *** ERROR: IOSurfaceCreate 'RGBA' failed - clientAddress: 0x14d6a0000 allocSize: 0x00072000  size: 256 x 456  rb: 1024 [0x00000400]  bpp: 4

这是什么意思,根本原因是什么?是否需要限制对 PHCachingImageManager 的访问?

下面是一个类似于我的应用程序中的一个类,它在我的 iPhone SE2 上重现了这个问题。

import Foundation
import SwiftUI
import UIKit
import Photos

let thumbnailSize = CGSize(width: 90, height: 90)

struct PhotoSelectView: View {

    class ImageRowManager {
        
        let thumbnailImageRequestOptions: PHImageRequestOptions
        let cachingImageManager = PHCachingImageManager()
        
        var rows: [SelectableImageRow] = []

        init() {
            let options = PHImageRequestOptions()
            options.isSynchronous = true
            options.resizeMode = .fast
            options.deliveryMode = .highQualityFormat
            options.isNetworkAccessAllowed = false
            self.thumbnailImageRequestOptions = options
        }
        
        func add(row: SelectableImageRow) {
            self.rows.append(row)
        }
    }
    
    struct SelectableImageRow: Hashable {
        
        var rowIndex: Int
        var images: [SelectableImage]

        func hash(into hasher: inout Hasher) {
            hasher.combine(self.rowIndex)
        }
    }

    class SelectableImage: Hashable, ObservableObject {
        
        @Published var image: UIImage? = nil

        let id: String
        private let asset: PHAsset
        private let imageRowManager: ImageRowManager
        
        init(asset: PHAsset, imageRowManager: ImageRowManager) {
            self.id = asset.localIdentifier
            self.asset = asset
            self.imageRowManager = imageRowManager
            self.loadImage()
        }
        
        func loadImage() {
            DispatchQueue.global(qos: .background).async {

                self.imageRowManager.cachingImageManager.requestImage(for: self.asset, targetSize: CGSize(width: 150, height: 150), contentMode: .aspectFill, options: self.imageRowManager.thumbnailImageRequestOptions) { (image, _) in
                    RunLoop.main.perform {
                        self.image = image
                    }
                }
            }
        }

        func hash(into hasher: inout Hasher) {
            hasher.combine(self.id)
        }

        static func ==(lhs: SelectableImage, rhs: SelectableImage) -> Bool {
            return lhs.id == rhs.id
        }
    }

    let imageRowManager = ImageRowManager()
    
    @State var selected = Set<SelectableImage>()
    @State var grid: [SelectableImageRow] = []


    var body: some View {

        VStack {

            VStack {

                if !self.grid.isEmpty {

                    HStack {
                        Text("Pick images")
                        Spacer()
                    }
                    .padding(.leading)
                    .padding(.top)
                    ImagesScrollView(grid: self.$grid, selected: self.$selected)
                    
                    Button(action: {
                        self.handleSelectButton()
                    }) {
                        
                        Text("Select")
                            .foregroundColor(Color.black.opacity((self.selected.count != 0) ? 1 : 0.5))
                            .padding(.vertical,10)
                            .frame(width: UIScreen.main.bounds.width / 2)
                            .overlay(
                                Capsule(style: .continuous)
                                    .stroke(Color.black.opacity((self.selected.count != 0) ? 1 : 0.5), style: StrokeStyle(lineWidth: 5))
                            )
                    }
                    .background(Color.white)
                    .padding(.bottom)
                    .disabled((self.selected.count != 0) ? false : true)

                }
            }
            .frame(width: UIScreen.main.bounds.width - CGFloat(horizontalPadding), height: UIScreen.main.bounds.height / 1.5)
            .background(Color.white)
            .cornerRadius(12)

        }
        .background(Color.black.opacity(0.1)).edgesIgnoringSafeArea(.all)
        .onAppear {
            PHPhotoLibrary.requestAuthorization { status in
                if status == .authorized {
                    self.getAllImages()
                } else {
                    print("Cannot access photo library")
                }
            }
        }

    }

    private func handleSelectButton() {
        print("selected images", self.selected)
    }

    private func getAllImages() {
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        let req = PHAsset.fetchAssets(with: .image, options: fetchOptions)
        var rowIndex = 0
        for i in stride(from: 0, to: req.count, by: gridItemWidth) {
            var iteration : [SelectableImage] = []
            for j in i..<i+gridItemWidth {
                if j < req.count{
                    iteration.append(SelectableImage(asset: req[j], imageRowManager: self.imageRowManager))
                }
            }
            let row = SelectableImageRow(rowIndex: rowIndex, images: iteration)
            imageRowManager.add(row: row)
            rowIndex += 1
        }
        
        self.grid = imageRowManager.rows
    }
    
    
    // Subviews
    
    struct ImagesScrollView: View {
        
        @Binding var grid: [SelectableImageRow]
        @Binding var selected: Set<SelectableImage>

        var body: some View {
            List(self.grid, id: \.self) { row in
                SelectableImageRowView(row: row, selected: self.$selected)
            }
        }
    }

    struct SelectableImageRowView: View {
        
        var row: SelectableImageRow
        @Binding var selected: Set<SelectableImage>

        var body: some View {
            HStack(spacing: 2) {
                ForEach(row.images, id: \.self) { img in
                    SelectableImageCard(data: img, selected: self.$selected)
                }
            }
        }
    }
    
    struct SelectableImageCard: View {

        @ObservedObject var data: SelectableImage
        @Binding var selected: Set<SelectableImage>

        var body: some View {

            ZStack {
                
                Image(uiImage: self.image()).resizable()
                
                if self.selected.contains(self.data) {
                    Image(systemName: "checkmark")
                        .resizable()
                        .padding(7)
                        .foregroundColor(.white)
                        .background(Color.blue)
                        .clipShape(Circle())
                        .overlay(Circle().stroke(Color.white, lineWidth: 1))
                        .frame(width: 30, height: 30, alignment: .topTrailing)
                        .offset(x: 30, y: -28)
                }
            }
            .frame(width: thumbnailSize.width, height: thumbnailSize.height)
            .onTapGesture {
                if !self.selected.contains(self.data) {
                    self.selected.insert(self.data)
                } else{
                    self.selected.remove(self.data)
                }
            }
        }

        private func image() -> some UIImage {
            if let image = self.data.image {
                return image
            } else {
                return UIImage(systemName: "heart.fill")!
            }
        }
    }
}

标签: iosswiftswiftuiphotokit

解决方案


推荐阅读