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) }
关键改进点说明
- 图片有效性验证:通过
UIImage(data: data)是否为nil,判断下载的数据是否是可显示的有效图片,这是解决你问题的核心。 - 串行请求逻辑:不再一次性发起所有请求,而是成功加载一张有效图片后,再发起下一个请求,遇到无效内容立刻停止,避免浪费网络资源。
- HTTP状态码检查:额外验证请求的状态码,覆盖服务器返回错误状态码的场景(比如404)。
内容的提问来源于stack exchange,提问作者JPJerry5
相关产品推荐
相关产品推荐

