Topic_ImageDataProvider.md 3.8 KB

Understanding the ImageDataProvider

Loading a local image or loading from data.

Overview

Kingfisher supports setting images from a local data source, allowing you to leverage its features for processing and managing local image data, bypassing the need for network downloads.

This allows for uniform API calls for both remote and local images, facilitating the reuse of familiar concepts, such as existing processors and cache serializers.

Image from local file

LocalFileImageDataProvider is a type that conforms to ImageDataProvider. It is specifically designed for loading images from local file URLs:

let url = URL(fileURLWithPath: path)
let provider = LocalFileImageDataProvider(fileURL: url)
imageView.kf.setImage(with: provider)

You can also pass options to it:

let processor = RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: provider, options: [.processor(processor)])

Image from Base64 string

Utilize Base64ImageDataProvider to supply an image from base64 encoded string. All standard features, including caching and image processing, function identically to how they operate when retrieving images via a URL.

let provider = Base64ImageDataProvider(base64String: "\/9j\/4AAQSkZJRgABAQA...", cacheKey: "some-cache-key")
imageView.kf.setImage(with: provider)

Generating image from AVAsset

Employ AVAssetImageDataProvider to create an image from a video URL or AVAsset at a specified time, leveraging Kingfisher's capabilities for handling video-based image sources.

let provider = AVAssetImageDataProvider(
    assetURL: URL(string: "https://example.com/your_video.mp4")!,
    seconds: 15.0
)

Creating a customize ImageDataProvider

To create your own image data provider, implement the ImageDataProvider protocol. This requires implementing a ImageDataProvider/cacheKey for unique identification and a ImageDataProvider/data(handler:) method to supply image data:

struct UserNameLetterIconImageProvider: ImageDataProvider {
    var cacheKey: String { return letter }
    let letter: String
    
    init(userNameFirstLetter: String) {
        self.letter = userNameFirstLetter
    }
    
    func data(handler: @escaping (Result<Data, any Error>) -> Void) {
        
        // You can ignore these detail below.
        // It generates some data for an image with `letter` being rendered in the center.

        let rect = CGRect(x: 0, y: 0, width: 250, height: 250)
        let renderer = UIGraphicsImageRenderer(size: rect.size)
        let data = renderer.pngData { context in
            UIColor.systemYellow.setFill()
            context.fill(rect)
            
            let attributes = [
                NSAttributedString.Key.foregroundColor: UIColor.white,
                                      .font: UIFont.systemFont(ofSize: 200)
            ]
            
            let textSize = letter.size(withAttributes: attributes)
            let textRect = CGRect(
                x: (rect.width - textSize.width) / 2,
                y: (rect.height - textSize.height) / 2,
                width: textSize.width,
                height: textSize.height)
            letter.draw(in: textRect, withAttributes: attributes)
        }

        // Provide the image data in `handler`.
        handler(.success(data))
    }
}

// Set image for user "John"
let provider = UserNameLetterIconImageProvider(userNameFirstLetter: "J")
imageView.kf.setImage(
    with: provider,
    options: [.processor(RoundCornerImageProcessor(radius: .point(75)))]
)

This generates a result like:

@Image(source: imagedataprovider-sample)

You might have noticed that ImageDataProvider/data(handler:) includes a callback. This allows you to supply the image data asynchronously from a different thread, which is useful if processing on the main thread is too heavy.