Как можно асинхронно загрузить UIImage в образ SwiftUI? - PullRequest
5 голосов
/ 11 июня 2019

В SwiftUI есть несколько .init методов для создания образа, но ни один из них не допускает блок или любой другой способ загрузки UIImage из сети / кэша ...

Я использую Kingfisher для загрузки изображений из сети и кэширования в строке списка, но способ отрисовки изображения в представлении состоит в том, чтобы повторно отрендерить его, чего я бы предпочел не делать , Кроме того, я создаю поддельное изображение (только цветное) в качестве заполнителя, пока изображение извлекается. Другим способом было бы обернуть все внутри пользовательского представления и перерисовать только оболочку. Но я еще не пробовал.

Этот образец работает прямо сейчас. Любая идея улучшить текущую будет отличной

Некоторый вид с использованием загрузчика

struct SampleView : View {

    @ObjectBinding let imageLoader: ImageLoader

    init(imageLoader: ImageLoader) {
        self.imageLoader = imageLoader
    }

    var body: some View {
       Image(uiImage: imageLoader.image(for: "https://url-for-image"))
          .frame(width: 128, height: 128)
          .aspectRatio(contentMode: ContentMode.fit)
    }

}
import UIKit.UIImage
import SwiftUI
import Combine
import class Kingfisher.ImageDownloader
import struct Kingfisher.DownloadTask
import class Kingfisher.ImageCache
import class Kingfisher.KingfisherManager

class ImageLoader: BindableObject {

    var didChange = PassthroughSubject<ImageLoader, Never>()
    private let downloader: ImageDownloader
    private let cache: ImageCache
    private var image: UIImage? {
        didSet {
            dispatchqueue.async { [weak self] in
                guard let self = self else { return }
                self.didChange.send(self)
            }
        }
    }
    private var task: DownloadTask?
    private let dispatchqueue: DispatchQueue

    init(downloader: ImageDownloader = KingfisherManager.shared.downloader,
         cache: ImageCache = KingfisherManager.shared.cache,
         dispatchqueue: DispatchQueue = DispatchQueue.main) {
        self.downloader = downloader
        self.cache = cache
        self.dispatchqueue = dispatchqueue
    }

    deinit {
        task?.cancel()
    }

    func image(for url: URL?) -> UIImage {
        guard let targetUrl = url else {
            return UIImage.from(color: .gray)
        }
        guard let image = image else {
            load(url: targetUrl)
            return UIImage.from(color: .gray)
        }
        return image
    }

    private func load(url: URL) {
        let key = url.absoluteString
        if cache.isCached(forKey: key) {
            cache.retrieveImage(forKey: key) {  [weak self] (result) in
                guard let self = self else { return }
                switch result {
                case .success(let value):
                    self.image = value.image
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
        } else {
            downloader.downloadImage(with: url, options: nil, progressBlock: nil) {  [weak self] (result) in
                guard let self = self else { return }
                switch result {
                case .success(let value):
                    self.cache.storeToDisk(value.originalData, forKey: url.absoluteString)
                    self.image = value.image
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
        }
    }

}

Ответы [ 2 ]

1 голос
/ 11 июня 2019

Определите imageLoader как @ObjectBinding:

@ObjectBinding private var imageLoader: ImageLoader

Было бы разумнее начать просмотр с URL-адреса изображения:

struct SampleView : View {

    var imageUrl: URL

    private var image: UIImage {
        imageLoader.image(for: imageUrl)
    }

    @ObjectBinding private var imageLoader: ImageLoader

    init(url: URL) {
        self.imageUrl = url
        self.imageLoader = ImageLoader()
    }

    var body: some View {
        Image(uiImage: image)
            .frame(width: 200, height: 300)
            .aspectRatio(contentMode: ContentMode.fit)
    }
}

Например:

//Create a SampleView with an initial photo
var s = SampleView(url: URL(string: "https://placebear.com/200/300")!)
//You could then update the photo by changing the imageUrl
s.imageUrl = URL(string: "https://placebear.com/200/280")!
0 голосов
/ 01 июля 2019

Передайте вашу модель в структуру ImageRow, которая содержит URL.

import SwiftUI
import Combine

struct ContentView : View {
    var listData: Post
    var body: some View {
        List(model.post) { post in
            ImageRow(model: post) // Get image
        }
    }
}

/********************************************************************/
// Download Image

struct ImageRow: View {
    let model: Post
    var body: some View {
        VStack(alignment: .center) {
            ImageViewContainer(imageUrl: model.avatar_url)
        }
    }
}

struct ImageViewContainer: View {
    @ObjectBinding var remoteImageURL: RemoteImageURL

    init(imageUrl: String) {
        remoteImageURL = RemoteImageURL(imageURL: imageUrl)
    }

    var body: some View {
        Image(uiImage: UIImage(data: remoteImageURL.data) ?? UIImage())
            .resizable()
            .clipShape(Circle())
            .overlay(Circle().stroke(Color.black, lineWidth: 3.0))
            .frame(width: 70.0, height: 70.0)
    }
}

class RemoteImageURL: BindableObject {
    var didChange = PassthroughSubject<Data, Never>()
    var data = Data() {
        didSet {
            didChange.send(data)
        }
    }
    init(imageURL: String) {
        guard let url = URL(string: imageURL) else { return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }

            DispatchQueue.main.async { self.data = data }

            }.resume()
    }
}
/********************************************************************/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...