在
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-Web
SwitUI-实现URL图片显示