Kingfisher: Desenvolvimento de Provedores de Dados e Processadores de Imagem Personalizados

Introdução aos Mecanismos de Extensão

O Kingfisher oferece arquitetura flexível para carregamento e cache de imagens em Swift. A biblioteca suporta fontes de dados variadas e operações de processamento através de protocolos bem definidos. Este guia explora a criação de implementações personalizadas para atender requisitos específicos de projeto.

Configuração Inicial do Ambiente

Integre o framework via Swift Package Manager utilizando o repositório oficial. Após integração, importe o módulo nos arquivos necessários:

import Kingfisher

Implementando ImageDataProvider Personalizado

O protocolo ImageDataProvider define o contrato para fornecimento de dados de imagem. Exige duas propriedades: cacheKey para identificação única e data(handler:) para entrega assíncrona dos dados.

Provedores de Dados Integradso

A biblioteca inclui provedores prontos para cenários comuns:

// Carregamento de arquivo local
guard let caminhoLocal = URL(string: "file:///imagens/perfil.jpg") else { return }
let provedorArquivo = LocalFileImageDataProvider(fileURL: caminhoLocal)
minhaImageView.kf.setImage(with: provedorArquivo)

// Decodificação Base64
let dadosBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
let provedorBase64 = Base64ImageDataProvider(base64String: dadosBase64, cacheKey: "imagem-base64-001")
imagem.kf.setImage(with: provedorBase64)

// Extração de frame de vídeo
if let urlVideo = URL(string: "https://midia.exemplo.com/clipe.mp4") {
    let provedorFrame = AVAssetImageDataProvider(assetURL: urlVideo, seconds: 5.0)
    preview.kf.setImage(with: provedorFrame)
}

Criando um Provedor de Dados Customizado

Deesnvolva um gerador de avatares com iniciais do usuário:

struct ProvedorAvatarIniciais: ImageDataProvider {
    let cacheKey: String
    private let iniciais: String
    private let corFundo: UIColor
    
    init(iniciaisUsuario: String, cor: UIColor = .systemIndigo) {
        self.iniciais = iniciaisUsuario
        self.corFundo = cor
        self.cacheKey = "avatar_\(iniciaisUsuario)_\(cor.hashValue)"
    }
    
    func data(handler: @escaping (Result<Data, Error>) -> Void) {
        DispatchQueue.global(qos: .userInteractive).async {
            let dimensoes = CGSize(width: 180, height: 180)
            let renderizador = UIGraphicsImageRenderer(size: dimensoes)
            
            let dadosImagem = renderizador.pngData { contexto in
                // Desenhar círculo de fundo
                let caminho = UIBezierPath(ovalIn: CGRect(origin: .zero, size: dimensoes))
                self.corFundo.setFill()
                caminho.fill()
                
                // Configurar atributos do texto
                let atributos: [NSAttributedString.Key: Any] = [
                    .font: UIFont.monospacedSystemFont(ofSize: 72, weight: .black),
                    .foregroundColor: UIColor.white
                ]
                
                // Centralizar texto
                let tamanhoTexto = self.iniciais.size(withAttributes: atributos)
                let pontoTexto = CGPoint(
                    x: (dimensoes.width - tamanhoTexto.width) / 2,
                    y: (dimensoes.height - tamanhoTexto.height) / 2
                )
                
                let retanguloTexto = CGRect(origin: pontoTexto, size: tamanhoTexto)
                self.iniciais.draw(in: retanguloTexto, withAttributes: atributos)
            }
            
            handler(.success(dadosImagem))
        }
    }
}

Utilização:

let provedorPersonalizado = ProvedorAvatarIniciais(iniciaisUsuario: "AB", cor: .systemTeal)
minhaImageView.kf.setImage(with: provedorPersonalizado)

Desenvolvendo ImageProcessor Customizado

O protocolo ImageProcessor permite transformar imagens através do método process(item:options:). Cada processador deve possuir um identificador único para controle de cache.

Processadores Integrados

// Redimensionamento com cantos arredondados
let procRedim = ResizingImageProcessor(referenceSize: CGSize(width: 120, height: 120))
let procCantos = RoundCornerImageProcessor(cornerRadius: 18)
let procCombinado = procRedim |> procCantos

// Aplicação de desfoque
let procDesfoque = BlurImageProcessor(blurRadius: 3)
imagem.kf.setImage(with: url, options: [.processor(procDesfoque)])

// Colorização
let procTonalidade = TintImageProcessor(tint: .systemOrange)

Implementação de Processador de Filtro Vintage

struct ProcessadorFiltroVintage: ImageProcessor {
    let identifier = "com.seuapp.ProcessadorFiltroVintage"
    
    func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
        switch item {
        case .image(let imgOriginal):
            guard let ciImg = CIImage(image: imgOriginal) else { return imgOriginal }
            
            // Aplicar controle de cores para efeito vintage
            let filtro = CIFilter(name: "CIColorControls")
            filtro?.setValue(ciImg, forKey: kCIInputImageKey)
            filtro?.setValue(1.3, forKey: "inputSaturation") // Saturação aumentada
            filtro?.setValue(0.1, forKey: "inputBrightness") // Leve brilho
            
            guard let imgSaida = filtro?.outputImage else { return imgOriginal }
            
            let contextoCI = CIContext()
            if let cgImagem = contextoCI.createCGImage(imgSaida, from: imgSaida.extent) {
                return KFCrossPlatformImage(cgImage: cgImagem)
            }
            return imgOriginal
            
        case .data(let dados):
            guard let imgDecodificada = KFCrossPlatformImage(data: dados) else { return nil }
            return process(item: .image(imgDecodificada), options: options)
        }
    }
}

Aplicação do processador:

let processadorVintage = ProcessadorFiltroVintage()
suaImageView.kf.setImage(
    with: URL(string: "https://exemplo.com/foto.jpg"),
    options: [.processor(processadorVintage)]
)

Estratégias Avançadas de Otimização

Gerenciamento de Cache Eficiente

Incorpore parâmetros no identificador para evitar colisões:

struct ProcessadorComParametros: ImageProcessor {
    let identifier: String
    
    init(intensidade: Double) {
        identifier = "processador.custom.\(intensidade)"
    }
    
    func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
        // Implementação do processamento
        return nil
    }
}

Armazene versões originais e processadas simultaneamente:

let processadorIntenso = ProcessadorComParametros(intensidade: 2.0)
imagem.kf.setImage(
    with: urlRemota,
    options: [
        .processor(processadorIntenso),
        .cacheOriginalImage
    ]
)

Dicas de Performance

Utilize DownsamplingImageProcessor para imagens de alta resolução:

let procAmostragem = DownsamplingImageProcessor(size: CGSize(width: 300, height: 300))
let procFinal = procAmostragem |> RoundCornerImageProcessor(cornerRadius: 12)

Instancie processadores frequentemente usados como propriedades estáticas:

extension ResizingImageProcessor {
    static let padraoPerfil = ResizingImageProcessor(referenceSize: CGSize(width: 80, height: 80))
}

Execute operações custosas em filas de background e evite processamento redundante verificando o cache antes de aplicar transformações complexas.

Tags: Kingfisher swift iOS Development ImageDataProvider ImageProcessor

Publicado em 10-10 03:02