
在SwiftUI
中,通过Image
来展示图片,例如:
Image("touxiang") Image(systemName: "arkit") .foregroundColor(.orange) .font(.system(size: 48)) Image("touxiang") .resizable() .aspectRatio(contentMode: .fit) Image(uiImage: UIImage(named: "touxiang")) .resizable() .frame(width: 50, height: 50)
以上都是使用Image
加载本地图片资源,但是SwiftUI
中的Image
没有提供直接加载URL
方式的图片显示,那么如何在SwiftUI
中让Image
加载网络图片呢,可以采用异步加载网络图片数据,由data
转换成UIimage
,再给Image
展示
struct ContentView : View { @State private var remoteImage : UIImage? = nil let placeholderOne = UIImage(named: "Picture") var body: some View { Image(uiImage: self.remoteImage ?? placeholderOne!) .onAppear(perform: fetchRemoteImage) } func fetchRemoteImage() { guard let url = URL(string: "http://hdjc8.com/images/logo.png") else { return } URLSession.shared.dataTask(with: url){ (data, response, error) in if let image = UIImage(data: data!){ self.remoteImage = image } else{ print(error ?? "") } }.resume() } }
但是这种异步加载图片的方式在Widget
中却失效了,Image
显示不了图片。
在TimelineProvider
的getTimeline
中completion(timeline)
执行完之后,不再支持图片的异步回调了,所以必须在数据请求回来的处理中采用同步方式,将图片的data
获取,转换成UIimage
,在赋值给Image
展示。
接下里给iOS14 Widget小组件开发实践2——自定义Widget里搭建的古诗视图增加一个网络封面图片显示,效果如下:

根据上面说的,既然异步加载图片不行了,必须在数据请求回来的处理中采用同步方式,将图片的data
获取,转换成UIimage
。那么我们先来在Poetry
里面增加一个UIimage
参数:
struct Poetry { let content: String // 内容 let origin: String // 名字 let author: String // 作者 var icon: UIImage? = UIImage(named: "touxiang") // 图片 }
因为这个免费的API
接口没有返回图片封面数据,所以就自己网上找个图片用来测试。关于图片请求的时机,这里我是将它放在了API
接口回调后处理json
转model
的这一步:
static func poetryFromJson(fromData data: Data) -> Poetry { ...... }
static func modelFromJson(fromData data: Data) -> Poetry { let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] //因为免费接口请求频率问题,如果太频繁了,请求可能失败,这里做下处理,放置crash guard let data = json["data"] as? [String: Any] else { return Poetry(content: "诗词加载失败,请稍微再试!", origin: "耐依福", author: "佚名") } let content = data["content"] as! String let origin = data["origin"] as! String let author = data["author"] as! String //这里加入对图片进行同步请求 var image: UIImage? = nil if let iamgeData = try? Data(contentsOf: URL(string: "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1058052610,2039041423&fm=26&gp=0.jpg")!) { image = UIImage(data: iamgeData) } return Poetry(content: content, origin: origin, author: author, icon: image) }
最后在给PoetryWidgetView
布局界面:
struct PoetryWidgetView : View { let entry: PoetryEntry var body: some View { HStack(content: { Image(uiImage: entry.poetry.icon!) .resizable() .frame(width: 70, height: 70) VStack(alignment: .leading, spacing: 4) { Text(entry.poetry.origin) .font(.system(size: 20)) .fontWeight(.bold) Text(entry.poetry.author) .font(.system(size: 16)) Text(entry.poetry.content) .font(.system(size: 18)) } }) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading) .padding() .background(LinearGradient(gradient: Gradient(colors: [.init(red: 144 / 255.0, green: 252 / 255.0, blue: 231 / 255.0), .init(red: 50 / 204, green: 188 / 255.0, blue: 231 / 255.0)]), startPoint: .topLeading, endPoint: .bottomTrailing)) } }
参考资料
https://github.com/fzhlee/SwiftUI-Guide#20Image-WebSwitUI-实现URL图片显示