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.