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

Swift 5中如何判断URL是否下载到有效图片?解决循环加载问题

解决方案:检测有效图片并停止无效请求

你的问题核心在于:当服务器返回非图片格式的有效数据时(比如空白内容),URLSession不会抛出错误,而且data也存在,但此时UIImage(data: data)会返回nil——这就是你看到"链接能加载但无图片"的原因。另外你当前的代码是一次性发起所有请求,即使中途发现无效图片,后续请求已经发出去了,所以修改test变量也无法停止循环。

下面给出两种可行的改造方案,核心都是验证下载的数据能否生成有效UIImage,同时改成串行请求(逐个验证,无效即停):

方案1:用Swift 5.5+的Async/Await(推荐)

这种方式代码更简洁,天然支持串行逻辑:

func setupScreens() async {
    var currentIndex = 1
    while true {
        let imageUrl = URL(string: "https://jarisstoriesphotographyphoto.files.wordpress.com/2020/06/menu\(currentIndex).png")!
        
        do {
            let (data, response) = try await URLSession.shared.data(from: imageUrl)
            
            // 第一步:验证HTTP请求是否成功(避免404/500等状态码)
            guard let httpResponse = response as? HTTPURLResponse, 
                  (200...299).contains(httpResponse.statusCode) else {
                print("请求失败,状态码:\((response as? HTTPURLResponse)?.statusCode ?? -1)")
                break
            }
            
            // 第二步:验证数据能否生成有效图片(核心步骤)
            guard let validImage = UIImage(data: data) else {
                print("无效图片数据,停止请求,当前索引:\(currentIndex)")
                break
            }
            
            // 主线程更新UI(必须保证UI操作在主线程)
            await MainActor.run {
                let imageFrame = CGRect(
                    x: scrollView.frame.width * CGFloat(currentIndex - 1),
                    y: 0,
                    width: scrollView.frame.width,
                    height: scrollView.frame.height
                )
                let imageView = UIImageView(frame: imageFrame)
                imageView.image = validImage
                scrollView.addSubview(imageView)
                
                // 更新ScrollView的内容尺寸,适配PageControl
                scrollView.contentSize = CGSize(
                    width: scrollView.frame.width * CGFloat(currentIndex),
                    height: scrollView.frame.height
                )
            }
            
            currentIndex += 1
        } catch {
            print("网络请求错误:\(error)")
            break
        }
    }
}

// 在viewDidLoad中调用(需要放在async上下文里)
override func viewDidLoad() {
    super.viewDidLoad()
    Task {
        await setupScreens()
        // 这里可以根据currentIndex设置PageControl的页数
        // pageControl.numberOfPages = currentIndex - 1
    }
}

方案2:用递归Completion Handler(兼容Swift 5.5以下)

如果你的项目还在用旧版本Swift,可以用递归方式实现串行请求:

func setupScreens(startingAt index: Int) {
    let imageUrl = URL(string: "https://jarisstoriesphotographyphoto.files.wordpress.com/2020/06/menu\(index).png")!
    
    let dataTask = URLSession.shared.dataTask(with: imageUrl) { [weak self] data, response, error in
        guard let self = self else { return }
        
        // 处理网络错误
        if let error = error {
            print("请求错误:\(error)")
            return
        }
        
        // 验证HTTP状态码
        guard let httpResponse = response as? HTTPURLResponse, 
              (200...299).contains(httpResponse.statusCode) else {
            print("请求失败,状态码:\((response as? HTTPURLResponse)?.statusCode ?? -1)")
            return
        }
        
        guard let imageData = data else {
            print("无数据返回,当前索引:\(index)")
            return
        }
        
        // 核心:验证图片有效性
        guard let validImage = UIImage(data: imageData) else {
            print("无效图片数据,停止请求,当前索引:\(index)")
            return
        }
        
        // 主线程更新UI
        DispatchQueue.main.async {
            let imageFrame = CGRect(
                x: self.scrollView.frame.width * CGFloat(index - 1),
                y: 0,
                width: self.scrollView.frame.width,
                height: self.scrollView.frame.height
            )
            let imageView = UIImageView(frame: imageFrame)
            imageView.image = validImage
            self.scrollView.addSubview(imageView)
            
            self.scrollView.contentSize = CGSize(
                width: self.scrollView.frame.width * CGFloat(index),
                height: self.scrollView.frame.height
            )
            
            // 递归发起下一个请求
            self.setupScreens(startingAt: index + 1)
        }
    }
    dataTask.resume()
}

// 在viewDidLoad中调用
override func viewDidLoad() {
    super.viewDidLoad()
    setupScreens(startingAt: 1)
}

关键改进点说明

  1. 图片有效性验证:通过UIImage(data: data)是否为nil,判断下载的数据是否是可显示的有效图片,这是解决你问题的核心。
  2. 串行请求逻辑:不再一次性发起所有请求,而是成功加载一张有效图片后,再发起下一个请求,遇到无效内容立刻停止,避免浪费网络资源。
  3. HTTP状态码检查:额外验证请求的状态码,覆盖服务器返回错误状态码的场景(比如404)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 08:43:14