Swift 4.1从服务器获取图片失败,求解决方案及代码修正
从服务器加载图片到UIImageView/UITableView的问题排查与修复
问题根源梳理
首先看你用Postman测试的初始API返回——它返回的是HTML <img>标签片段+结尾的JSON,完全不是直接的图片二进制数据:
<img src='http://localhost/Book-My-BIM-API/Company-Registration- API/v1/companyImages/harshilpatel@gmail.com_company-registration.jpg' style='height:40%;width:40%;'/> <img src='http://localhost/Book-My-BIM-API/Company-Registration- API/v1/companyImages/shriyakshah@gmail.com_company-registration.jpg' style='height:40%;width:40%;'/> ... {"message":"Image displayed Successfully"}
所以你最初的代码直接把data转成UIImage肯定失败,因为那根本不是图片数据。
后来你更新了API,改成了更规范的JSON结构(虽然你贴的内容不完整,但能看出是包含logos数组,每个元素带company_logo图片URL的格式):
{ "logos": [ { "company_logo": "http://172.16.1.22/Book-My-BIM-API/Company-Registration-API/v1/companyImages/harshilpatel@gmail.com_company-registration.
这是正确的方向,但你的代码还有几个关键问题:
- 缺失匹配JSON结构的Codable模型定义(你用了
ImageData和Images但没贴,大概率和API返回不匹配) - TableView cell复用导致的图片错位问题:异步加载完成后,cell可能已经被复用给其他行
- 用
try? Data(contentsOf:)同步加载图片,既没有错误处理,还会阻塞线程 - 服务器返回的URL用了
localhost,模拟器/真机无法访问,需要改成局域网IP(比如你之前用的172.16.1.22)
完整修复方案
1. 定义匹配JSON结构的Codable模型
根据你更新后的API返回,假设完整的JSON结构是:
{ "logos": [ { "company_logo": "http://172.16.1.22/Book-My-BIM-API/Company-Registration-API/v1/companyImages/xxx.jpg" } ], "message": "Image displayed Successfully" }
对应的Swift模型:
import Foundation // 顶层响应模型,匹配API返回的根结构 struct ImageResponse: Codable { let logos: [ImageData] let message: String } // 单个图片数据模型,匹配logos数组中的元素 struct ImageData: Codable { let company_logo: String }
2. 修正网络请求代码
确保正确解析JSON,并且回到主线程更新UI:
func downloadImage(){ guard let downloadURL = URL(string: "http://172.16.1.22/UploadImage/displayImage.php") else{ print("无效的请求URL") return } URLSession.shared.dataTask(with: downloadURL) { (data, response, error) in // 处理请求错误 if let error = error { print("请求失败:\(error.localizedDescription)") return } // 确保有返回数据 guard let data = data else { print("未接收到数据") return } do { let decoder = JSONDecoder() // 用定义好的模型解析JSON let imageResponse = try decoder.decode(ImageResponse.self, from: data) self.imageArray = imageResponse.logos // 必须回到主线程刷新TableView DispatchQueue.main.async { self.imageTableVieqw.reloadData() } } catch { print("JSON解析失败:\(error.localizedDescription)") // 打印详细错误信息,方便调试 print("错误详情:\(error)") } }.resume() }
3. 修复TableView Cell的图片加载问题
解决cell复用导致的图片错位,同时用异步加载替代阻塞线程的同步请求:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as! ImageTableViewCell // 先设置占位图,避免复用旧数据 cell.imageCell.image = UIImage(named: "placeholder") // 替换成你的占位图名称 let imageURLString = imageArray[indexPath.row].company_logo guard let imageURL = URL(string: imageURLString) else { print("无效的图片URL:\(imageURLString)") return cell } // 用URLSession异步加载图片,不阻塞主线程 URLSession.shared.dataTask(with: imageURL) { [weak cell] (data, _, error) in if let error = error { print("图片加载失败:\(error.localizedDescription)") return } guard let data = data, let image = UIImage(data: data) else { print("无法将数据转为图片") return } // 回到主线程更新UI,同时检查cell是否还对应当前行 DispatchQueue.main.async { guard let currentCell = cell, let currentIndexPath = tableView.indexPath(for: currentCell), currentIndexPath == indexPath else { // cell已经被复用,放弃更新 return } currentCell.imageCell.image = image } }.resume() return cell }
4. 解决HTTP访问限制
因为你用的是HTTP协议,iOS默认会阻止非HTTPS请求,需要在Info.plist中添加ATS例外(推荐用更安全的域名白名单方式):
<key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>172.16.1.22</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key> <true/> </dict> </dict> </dict>
额外优化建议
- 使用成熟的图片加载库(比如SDWebImage、Kingfisher),它们已经帮你处理了缓存、cell复用、错误处理等问题,代码会更简洁:
比如用SDWebImage的话,cell里的加载代码可以简化为:import SDWebImage // 在cellForRowAt中 cell.imageCell.sd_setImage(with: imageURL, placeholderImage: UIImage(named: "placeholder")) - 给图片添加本地缓存,避免重复请求相同图片
- 处理图片加载失败的情况(比如显示错误占位图)
内容的提问来源于stack exchange,提问作者Audrey Rozario
相关产品推荐
相关产品推荐

