Como os filtros UIScrollView / UICollectionView no aplicativo Photos da Apple são implementados para abrir tão rápido?
Não estou perguntando sobre o código exato, mas a ideia geral.
Aqui está o meu problema: estou tentando criar algo semelhante ao filtro, escolhendo a interface do usuário no aplicativo Fotos. Eu tentei várias abordagens e todas elas têm suas desvantagens.
1) Eu tentei usarOperation
eOperationQueue
com uma exibição de coleção, cuja pré-busca está ativada. Isso carrega o viewController rapidamente, mas descarta os quadros durante a rolagem.
2) No momento, estou usando uma visualização de rolagem eGCD
mas carrega o viewController por muito tempo (porque aplica todos os filtros a todos os botões dentro dele de uma só vez), mas depois rola suavemente.
NOTA: Para responder à pergunta, não há necessidade de ler a parte abaixo (acredito), no entanto, se você estiver interessado em saber como eu tento implementar a funcionalidade, leia-a.
Para a implementação de todos os filtros, eu uso uma estrutura chamadaFilters
responsável por iniciar cada filtro e anexá-lo a uma 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])
}
}
No momento, estou usando apenas um filtro.Sepia
é uma subclasse deCIFilter
. Eu o criei como uma subclasse, porque no futuro vou criar um personalizado a partir dele. Aqui está a sua implementação:
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
}
}
No viewController'sviewDidLoad
Eu inicioFilters
struct:
self.filters = Filters(image: image)
Então eu chamo um método que configura algumas visualizações (filterViews
) com base no número de filtros nofilters.allFilters
matriz e itera sobre eles e chama um método que leva uma miniaturaUIImage
e aplica um filtro a ele e o retorna em um manipulador de conclusão (eu usoDispatchGroup
por razões de depuração). Aqui está o método que aplica um filtro a uma 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")
}
}
A declaração de impressão acima e o endereço da imagem filtrada são impressos muito em breve, no entanto, as imagens não aparecem nas visualizações.
Eu tentei usarTime Profiler
mas isso me dá alguns resultados estranhos. Por exemplo, mostra o seguinte como demorando muito para ser executado na raiz do backtrace:
Quando tento ver o código no Xcode, recebo o seguinte, o que não ajuda muito:
Então, esse é o problema. Se você tiver alguma idéia de como ele é implementado no aplicativo Fotos, é tão rápido e responsivo, ou se você tem sugestões sobre minha implementação, agradecemos sua ajuda.