当我滚动SwiftUI / Swift时,图像会消失在列表中

时间:2020-03-16 17:59:41

标签: swift swiftui

因此,我要通过获取(获取)图像的路径,然后使用图像路径从该url /路径获取图像,将图像添加到我的SwiftUI列表中。

抓取数据/路径

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
        }
    }


}

从网址/路径获取图片

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图片

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图像在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 个答案:

答案 0 :(得分:2)

消失的图像是由于重复使用列表中的单元格,并且每次单元格重新使用它来再次获取图像时,都需要缓存图像以防止出现。

您可以通过这种方式更改ImageLoader并具有缓存 您还需要在ImageView中更改图像,而不是使用UIImage来代替数据

select t.*, pow(c1 - 8, 2) + pow(c2 - 5, 2) as distance_squared
from test t
order by distance_squared
limit 3 

缓存类助手

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
        }
    }
}

ImageView结构:

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
    }
}