1

我正在开发照片编辑器应用程序,但在 UICollectionView 中渲染过滤图像时遇到问题。我正在使用OperationOperationQueue。当我开始滚动 collectionView 时,过滤后的图像正在更新。我该如何解决?

图像过滤:

class ImageFiltration: Operation {
private let image: CIImage
private let filterName: String!

var imageWithFilter: CIImage?

init(image: CIImage, filterName: String) {
    self.image = image
    self.filterName = filterName
}

override func main() {
    if self.isCancelled {
        return
    }

    if let name = filterName {
        let filter = CIFilter(name: name, withInputParameters: [kCIInputImageKey: image])
        imageWithFilter = filter?.outputImage!
    }
} }

class PendingOperation {
lazy var filtrationInProgress = [IndexPath: Operation]()
lazy var filtrationQueue: OperationQueue = {
    var queue = OperationQueue()
    queue.name = "Filtration Operation"
    return queue
}() }

UIScrollViewDelegate:

extension PhotoEditorViewController: UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    presenter.suspendAllOperations()
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if !decelerate {
        presenter.loadImagesForOnScreenCells(collectionView: filtersToolsView.collectionView) { (indexPath) in
            self.filtersToolsView.collectionView.reloadItems(at: [indexPath])
        }
        presenter.resumeAllOperations()
    }
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    presenter.loadImagesForOnScreenCells(collectionView: filtersToolsView.collectionView) { (indexPath) in
        self.filtersToolsView.collectionView.reloadItems(at: [indexPath])
    }
    presenter.resumeAllOperations()
} }

UICollectionView 数据源:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell: PhotoEditorFilterCollectionViewCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)

    cell.layer.shouldRasterize = true
    cell.layer.rasterizationScale = UIScreen.main.scale
    cell.filteredImage.contentMode = .scaleAspectFill

    presenter.startFiltration(indexPath: indexPath) { (image, filterName) in
        cell.filteredImage.inputImage = image
        cell.filterNameLabel.text = filterName
    }

    return cell
}

实现启动过滤方法:

func startFiltration(indexPath: IndexPath, completion: @escaping (CIImage?, String) -> ()) {
    if let _ = pendingOperations.filtrationInProgress[indexPath] {
        return
    }

    let filteredImage = ImageFiltration(image: model.image,
                                        filterName: model.image.filters[indexPath.row].filterName)

    filteredImage.completionBlock = {
        if filteredImage.isCancelled {
            return
        }

        DispatchQueue.main.async {
            self.pendingOperations.filtrationInProgress.removeValue(forKey: indexPath)
            completion(filteredImage.imageWithFilter, self.model.image.filters[indexPath.row].filterDisplayName)
        }
    }

    pendingOperations.filtrationInProgress[indexPath] = filteredImage
    pendingOperations.filtrationQueue.addOperation(filteredImage)
}

操作方法:

func suspendAllOperations() {
    pendingOperations.filtrationQueue.isSuspended = true
}

func resumeAllOperations() {
    pendingOperations.filtrationQueue.isSuspended = false
}

func loadImagesForOnScreenCells(collectionView: UICollectionView,
                                completion: @escaping (IndexPath) -> ()) {
    let pathsArray = collectionView.indexPathsForVisibleItems

    let allPendingOperations = Set(Array(pendingOperations.filtrationInProgress.keys))
    let visiblePaths = Set(pathsArray as [IndexPath])
    var toBeCancelled = allPendingOperations

    toBeCancelled.subtract(visiblePaths)

    var toBeStarted = visiblePaths

    toBeStarted.subtract(allPendingOperations)

    for indexPath in toBeCancelled {
        if let pendingFiltration = pendingOperations.filtrationInProgress[indexPath] {
            pendingFiltration.cancel()
        }

        pendingOperations.filtrationInProgress.removeValue(forKey: indexPath)
    }

    for indexPath in toBeStarted {
        let indexPath = indexPath as IndexPath
        completion(indexPath)
    }
}

我在 GLKView 中渲染一张图片。

视频

4

1 回答 1

1

在 scrollViewDidScroll 上取消您的所有请求。最后滚动获得可见的单元格并启动您的操作队列。这样它只会使用你可见的单元格内存,并且 UI 不会卡住。请参阅本教程,它与您的要求相同 - https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift

于 2018-01-20T20:22:09.107 回答