Cómo se implementan los filtros UIScrollView / UICollectionView en la aplicación Fotos de Apple que se abre tan rápido?

No estoy preguntando por el código exacto sino por la idea general.

Aquí está mi problema: estoy tratando de crear algo similar al filtro eligiendo la interfaz de usuario en la aplicación Fotos. He intentado varios enfoques y todos tienen sus inconvenientes.

1) He intentado usarOperation yOperationQueue con una vista de colección, cuya captación previa está habilitada. Esto carga el viewController rápidamente, pero suelta fotogramas mientras se desplaza.

2) En este momento estoy usando una vista de desplazamiento yGCD pero carga el viewController demasiado tiempo (porque aplica todos los filtros a todos los botones dentro de él a la vez), pero luego se desplaza suavemente.

NOTA Para responder la pregunta, no es necesario leer la parte siguiente (creo), sin embargo, si está interesado en cómo trato de implementar la funcionalidad, puede leerla.

Para la implementación de todos los filtros, uso una estructura llamadaFilters que es responsable de iniciar cada filtro y agregarlo a una matriz.

struct Filters {
var image: UIImage
var allFilters: [CIFilter] = []

init(image: UIImage) {

    self.image = image

    guard  let sepia = Sepia(image: image) else {return}

    allFilters.append(contentsOf: [sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia])

       }
  }

Ahora estoy usando solo un filtro. @Sepia es una subclase deCIFilter. Lo he creado como una subclase porque en el futuro voy a crear uno personalizado a partir de él. Aquí está su implementación:

class Sepia: CIFilter {

var inputImage: CIImage?
var inputIntensity: NSNumber?

@objc override var filterName: String? {
    return NSLocalizedString("Sepia", comment: "Name of a Filter")
}

convenience init?(image: UIImage, inputIntensity: NSNumber? = nil) {
    self.init()

    guard let cgImage = image.cgImage else {
        return nil
    }

    if inputIntensity != nil {
        self.inputIntensity = inputIntensity
    } else {
        self.setDefaults()
    }

    let inputImage = CIImage(cgImage: cgImage)
    self.inputImage = inputImage
}

override func setDefaults() {
    inputIntensity = 1.0
}

override var outputImage: CIImage? {
    guard let inputImage = inputImage, let inputIntensity = inputIntensity else {
        return nil
    }

    let filter = CIFilter(name: "CISepiaTone", withInputParameters: [kCIInputImageKey: inputImage, kCIInputIntensityKey: inputIntensity])

   return filter?.outputImage

  }
}

In the viewController'sviewDidLoad Yo inicioFilters struct:

self.filters = Filters(image: image)

Luego llamo a un método que configura algunas vistas filterViews) según el número de filtros en lafilters.allFilters array e itera sobre ellos y llama a un método que toma una miniaturaUIImage y le aplica un filtro y luego lo devuelve en un controlador de finalización (usoDispatchGroup en él por razones de depuración). Aquí está el método que aplica un filtro a una miniatura:

func imageWithFilter(filter: CIFilter, completion: @escaping(UIImage?)->Void) {

    let group = DispatchGroup()
    group.enter()
    DispatchQueue.global().async {
        guard let outputImage = filter.value(forKey: kCIOutputImageKey) as? CIImage, let cgImageResult = self.context.createCGImage(outputImage, from: outputImage.extent) else  {
            DispatchQueue.main.async {
                 completion(nil)
            }
            group.leave()
            return
        }

        let filteredImage = UIImage(cgImage: cgImageResult)
        DispatchQueue.main.async {
            print (filteredImage)
            completion(filteredImage)

        }

       group.leave()
    }

    group.notify(queue: .main) {
        print ("Filteres are set")
    }
}

La declaración de impresión anterior y la dirección de la imagen filtrada se imprimen muy pronto, sin embargo, las imágenes no aparecen dentro de las vistas.

He intentado usarTime Profiler pero me da algunos resultados extraños. Por ejemplo, muestra lo siguiente como una ejecución bastante larga en la raíz de la traza inversa:

Cuando trato de ver el código en Xcode, obtengo lo siguiente, lo que no ayuda mucho:

Entonces, este es el problema. Si tiene alguna idea de cómo se implementa en la aplicación Fotos que es tan rápida y receptiva, o si tiene sugerencias sobre mi implementación, agradecería su ayuda.