You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何让CollectionView更快加载JSON中的图片URL

Fixing Slow Image Loading in Your CollectionView

Hey there! I see exactly why your CollectionView is loading images so slowly—let's break down the issues and fix them step by step.

What's Causing the Slowdown?

  1. Synchronous Image Loading: You're using NSData(contentsOf:) directly in cellForItemAt, which blocks the main thread while downloading images. This means your UI freezes until each image finishes loading, even for just 10 items.
  2. No Caching: Every time a cell is reused (like when scrolling), you re-download the same image again. This wastes bandwidth and adds unnecessary delay.

Step-by-Step Fixes

1. Add an Async Image Loader with Caching

First, create a reusable image loader that handles async downloads and caches images in memory (so we don't re-download the same image over and over):

import UIKit

class ImageLoader {
    static let shared = ImageLoader()
    private let imageCache = NSCache<NSString, UIImage>()
    
    func loadImage(from urlString: String, completion: @escaping (UIImage?) -> Void) {
        // Validate the URL first
        guard let url = URL(string: urlString) else {
            completion(nil)
            return
        }
        
        // Check memory cache first
        if let cachedImage = imageCache.object(forKey: urlString as NSString) {
            completion(cachedImage)
            return
        }
        
        // Async download from network
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let data = data, let image = UIImage(data: data), error == nil else {
                completion(nil)
                return
            }
            
            // Cache the downloaded image
            self?.imageCache.setObject(image, forKey: urlString as NSString)
            
            // Update UI on main thread
            DispatchQueue.main.async {
                completion(image)
            }
        }.resume()
    }
}

2. Update Your CollectionView Cell Setup

Modify cellForItemAt to use the async loader, add a placeholder, and prevent image misalignment from cell reuse:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCollectionViewCell
    let aData = itemsArray[indexPath.row]
    
    // Set label text first
    cell.paymentLabel.text = aData.typeName
    
    // Set placeholder to avoid empty cells or old images
    cell.paymentImage.image = UIImage(named: "placeholder-image") // Replace with your own placeholder
    
    // Tag the cell to prevent image misalignment on reuse
    cell.tag = indexPath.row
    let imageUrl = aData.iconHome ?? ""
    
    // Load image asynchronously
    ImageLoader.shared.loadImage(from: imageUrl) { [weak cell] image in
        // Make sure we're updating the correct cell (not a reused one)
        guard let cell = cell, cell.tag == indexPath.row else { return }
        cell.paymentImage.image = image ?? UIImage(named: "placeholder-image")
    }
    
    return cell
}

3. Clean Up Your Service Call

Your service call has some unsafe force unwraps that could cause crashes. Let's make it more robust:

func homeServiceCall(){
    let urlStr = "https://dev.com/webservices"
    guard let url = URL(string: urlStr) else {
        print("Invalid URL for service call")
        return
    }
    
    URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
        guard let self = self else { return }
        guard let respData = data else {
            print("No data received from service")
            return
        }
        guard error == nil else {
            print("Service error: \(error!)")
            return
        }
        
        do {
            let jsonObj = try JSONSerialization.jsonObject(with: respData) as? [String: Any] ?? [:]
            guard let financerArray = jsonObj["financer"] as? [[String: Any]] else {
                print("Invalid financer array format in JSON")
                return
            }
            
            // Use temp arrays to avoid modifying UI-related arrays on background thread
            var tempItems = [JsonData]()
            var tempIds = [Int]()
            
            for financer in financerArray {
                let id = financer["id"] as? Int ?? 1
                let pic = financer["icon"] as? String ?? ""
                guard let typeName = financer["tpe"] as? String else { continue }
                
                tempIds.append(id)
                tempItems.append(JsonData(icon: pic, tpe: typeName))
            }
            
            // Update UI on main thread
            DispatchQueue.main.async {
                self.idArray = tempIds
                self.itemsArray = tempItems
                self.collectionView.reloadData()
            }
        } catch {
            print("JSON parsing error: \(error)")
        }
    }.resume()
}

Bonus: Use a Third-Party Library (Even Easier!)

If you want to skip writing custom caching logic, use a battle-tested library like Kingfisher or SDWebImage. For example, with Kingfisher:

  1. Add Kingfisher to your project via CocoaPods or Swift Package Manager
  2. Replace your image loading code in cellForItemAt with:
import Kingfisher

// ...

cell.paymentImage.kf.setImage(
    with: URL(string: aData.iconHome ?? ""),
    placeholder: UIImage(named: "placeholder-image")
)

Kingfisher handles async loading, memory/disk caching, placeholder images, and error handling out of the box—super convenient!

内容的提问来源于stack exchange,提问作者Swift

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:09:44