Изображения исчезают в списке при прокрутке SwiftUI / Swift - PullRequest
0 голосов
/ 16 марта 2020

Итак, я добавляю изображение в свой список SwiftUI, получая (выбирая) путь к изображению, а затем используя путь к изображению, чтобы получить изображение с этого URL / пути.

Захват данные / путь

class Webservice {
    func getAllPosts(completion: @escaping ([Post]) -> ()) {
        guard let url = URL(string: "http://localhost:8000/albums")
     else {
     fatalError("URL is not correct!")
    }

        URLSession.shared.dataTask(with: url) { data, _, _ in

            let posts = try!

                JSONDecoder().decode([Post].self, from: data!); DispatchQueue.main.async {
                    completion(posts)
            }
        }.resume()
    }
}

Переменные

struct Post: Codable, Hashable, Identifiable {

    let id: String
    let title: String
    let path: String
    let description: String
}

Установка переменных в Post для сообщений в completion(posts) в class Webservice.

final class PostListViewModel: ObservableObject {

    init() {
        fetchPosts()
    }

    @Published var posts = [Post]()

    private func fetchPosts() {
        Webservice().getAllPosts {
            self.posts = $0
        }
    }


}

Получить изображение с URL / пути

class ImageLoader: ObservableObject {
    var didChange = PassthroughSubject<Data, Never>()
    var data = Data() {
        didSet {
            didChange.send(data)
        }
    }

    init(urlString:String) {
        guard let url = URL(string: urlString) else { return }
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else { return }
            DispatchQueue.main.async {
                self.data = data
            }
        }

        task.resume()
    }
}

Установить @State image

struct ImageView: View {
    @ObservedObject var imageLoader:ImageLoader
    @State var image:UIImage = UIImage()

    init(withURL url:String) {
        imageLoader = ImageLoader(urlString:url)
    }

    var body: some View {

            Image(uiImage: image)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width:100, height:100)
                .onReceive(imageLoader.didChange) { data in
                self.image = UIImage(data: data) ?? UIImage()
        }
    }
}

Затем отобразите мое изображение в списке, используя ImageView с URL-адресом / путем из данных, которые я выбрал в начале, который передается в ImageLoader, чтобы получить изображение и установить @State image в ImageView

struct ContentView: View {
   init() {
            Webservice().getAllPosts {
                print($0)
            }
        }

    @ObservedObject var model = PostListViewModel()

        var body: some View {

                List(model.posts) { post in
                 VStack {
                    Text(post.title)
                    ImageView(withURL: "http://localhost:8000/\(post.path)")
                    Text(post.description)


                }
            }

        }

}

Этот код сначала работает для получения изображения, но как только я начинаю прокручивать здесь и там, изображения начинают исчезать. Почему это происходит и как я могу это исправить?

Редактировать: Мне сказали кэшировать изображения, чтобы предотвратить загрузку снова. Кто-нибудь знает, как я это сделаю?

1 Ответ

1 голос
/ 16 марта 2020

исчезновение изображения происходит из-за повторного использования ячеек в списке, и каждый раз, когда ячейка повторно использует его для получения изображения снова, вам необходимо кэшировать изображения, чтобы предотвратить его.

вы можете изменить свой ImageLoader в этом путь и иметь кеш, вам нужно изменить изображение в вашем ImageView тоже, вместо данных использовать UIImage

class ImageLoader: ObservableObject {
    @Published var image: UIImage?
    var urlString: String?
    var imageCache = ImageCache.getImageCache()

    init(urlString: String?) {
        self.urlString = urlString
        loadImage()
    }

    func loadImage() {
        if loadImageFromCache() {
            print("Cache hit")
            return
        }

        print("Cache miss, loading from url")
        loadImageFromUrl()
    }

    func loadImageFromCache() -> Bool {
        guard let urlString = urlString else {
            return false
        }

        guard let cacheImage = imageCache.get(forKey: urlString) else {
            return false
        }

        image = cacheImage
        return true
    }

    func loadImageFromUrl() {
        guard let urlString = urlString else {
            return
        }

        let url = URL(string: urlString)!
        let task = URLSession.shared.dataTask(with: url, completionHandler: getImageFromResponse(data:response:error:))
        task.resume()
    }


    func getImageFromResponse(data: Data?, response: URLResponse?, error: Error?) {
        guard error == nil else {
            print("Error: \(error!)")
            return
        }
        guard let data = data else {
            print("No data found")
            return
        }

        DispatchQueue.main.async {
            guard let loadedImage = UIImage(data: data) else {
                return
            }

            self.imageCache.set(forKey: self.urlString!, image: loadedImage)
            self.image = loadedImage
        }
    }
}

Cache class helper

class ImageCache {
    var cache = NSCache<NSString, UIImage>()

    func get(forKey: String) -> UIImage? {
        return cache.object(forKey: NSString(string: forKey))
    }

    func set(forKey: String, image: UIImage) {
        cache.setObject(image, forKey: NSString(string: forKey))
    }
}

extension ImageCache {
    private static var imageCache = ImageCache()
    static func getImageCache() -> ImageCache {
        return imageCache
    }
}

Структура ImageView:


struct ImageView: View {
    @ObservedObject var imageLoader:ImageLoader

    init(withURL url:String) {
        imageLoader = ImageLoader(urlString:url)
    }

    var body: some View {

            Image(imageLoader.image ?? UIImage() )
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width:100, height:100)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...