所以我正在尝试使用从我的 Node JS 服务器获取的数据创建一个内容提要。
在这里,我从我的 API 中获取数据
class Webservice {
func getAllPosts(completion: @escaping ([Post]) -> ()) {
guard let url = URL(string: "http://localhost:8000/albums")
else {
fatalError("URL is not correct!")
}
URLSession.shared.dataTask(with: url) { data, _, _ in
let posts = try!
JSONDecoder().decode([Post].self, from: data!); DispatchQueue.main.async {
completion(posts)
}
}.resume()
}
}
Run Code Online (Sandbox Code Playgroud)
将变量设置为从 API 获取的数据
final class PostListViewModel: ObservableObject {
init() {
fetchPosts()
}
@Published var posts = [Post]()
private func fetchPosts() {
Webservice().getAllPosts {
self.posts = $0
}
}
}
Run Code Online (Sandbox Code Playgroud)
struct Post: Codable, Hashable, Identifiable {
let id: String
let title: String
let path: String
let description: String
}
Run Code Online (Sandbox Code Playgroud)
用户界面
struct ContentView: View {
@ObservedObject var model = PostListViewModel()
var body: some View {
List(model.posts) { post in
HStack {
Text(post.title)
Image("http://localhost:8000/" + post.path)
Text(post.description)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
来自post.title和的文本post.description显示正确,但没有显示来自Image()。如何使用来自我的服务器的 URL 与我的图像一起显示?
Mac*_*c3n 45
AsyncImage(url: URL(string: "https://your_image_url_address"))
Run Code Online (Sandbox Code Playgroud)
有关 Apple 开发人员文档的更多信息: AsyncImage
首先,您需要从 url 获取图像:
class ImageLoader: ObservableObject {
var didChange = PassthroughSubject<Data, Never>()
var data = Data() {
didSet {
didChange.send(data)
}
}
init(urlString:String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
self.data = data
}
}
task.resume()
}
}
Run Code Online (Sandbox Code Playgroud)
你也可以把它作为你的 Webservice 类函数的一部分。
然后在您的 ContentView 结构中,您可以以这种方式设置 @State 图像:
struct ImageView: View {
@ObservedObject var imageLoader:ImageLoader
@State var image:UIImage = UIImage()
init(withURL url:String) {
imageLoader = ImageLoader(urlString:url)
}
var body: some View {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width:100, height:100)
.onReceive(imageLoader.didChange) { data in
self.image = UIImage(data: data) ?? UIImage()
}
}
}
Run Code Online (Sandbox Code Playgroud)
此外,如果您需要更多,本教程是一个很好的参考
Avi*_*oss 17
结合@naishta(iOS 13+)和@mrmins(占位符和配置)答案,加上公开Image(而不是UIImage)以允许配置它(调整大小,剪辑等)
使用示例:
var body: some View {
RemoteImageView(
url: someUrl,
placeholder: {
Image("placeholder").frame(width: 40) // etc.
},
image: {
$0.scaledToFit().clipShape(Circle()) // etc.
}
)
}
Run Code Online (Sandbox Code Playgroud)
struct RemoteImageView<Placeholder: View, ConfiguredImage: View>: View {
var url: URL
private let placeholder: () -> Placeholder
private let image: (Image) -> ConfiguredImage
@ObservedObject var imageLoader: ImageLoaderService
@State var imageData: UIImage?
init(
url: URL,
@ViewBuilder placeholder: @escaping () -> Placeholder,
@ViewBuilder image: @escaping (Image) -> ConfiguredImage
) {
self.url = url
self.placeholder = placeholder
self.image = image
self.imageLoader = ImageLoaderService(url: url)
}
@ViewBuilder private var imageContent: some View {
if let data = imageData {
image(Image(uiImage: data))
} else {
placeholder()
}
}
var body: some View {
imageContent
.onReceive(imageLoader.$image) { imageData in
self.imageData = imageData
}
}
}
class ImageLoaderService: ObservableObject {
@Published var image = UIImage()
convenience init(url: URL) {
self.init()
loadImage(for: url)
}
func loadImage(for url: URL) {
let task = URLSession.shared.dataTask(with: url) { data, _, _ in
guard let data = data else { return }
DispatchQueue.main.async {
self.image = UIImage(data: data) ?? UIImage()
}
}
task.resume()
}
}
Run Code Online (Sandbox Code Playgroud)
MrM*_*ins 12
试试这个实现:
AsyncImage(url: URL(string: "http://mydomain/image.png")!,
placeholder: { Text("Loading ...") },
image: { Image(uiImage: $0).resizable() })
.frame(idealHeight: UIScreen.main.bounds.width / 2 * 3) // 2:3 aspect ratio
Run Code Online (Sandbox Code Playgroud)
看起来很简单吧?此功能可以将图像保存在缓存中,也可以发出异步图像请求。
现在,将其复制到一个新文件中:
import Foundation
import SwiftUI
import UIKit
import Combine
struct AsyncImage<Placeholder: View>: View {
@StateObject private var loader: ImageLoader
private let placeholder: Placeholder
private let image: (UIImage) -> Image
init(
url: URL,
@ViewBuilder placeholder: () -> Placeholder,
@ViewBuilder image: @escaping (UIImage) -> Image = Image.init(uiImage:)
) {
self.placeholder = placeholder()
self.image = image
_loader = StateObject(wrappedValue: ImageLoader(url: url, cache: Environment(\.imageCache).wrappedValue))
}
var body: some View {
content
.onAppear(perform: loader.load)
}
private var content: some View {
Group {
if loader.image != nil {
image(loader.image!)
} else {
placeholder
}
}
}
}
protocol ImageCache {
subscript(_ url: URL) -> UIImage? { get set }
}
struct TemporaryImageCache: ImageCache {
private let cache = NSCache<NSURL, UIImage>()
subscript(_ key: URL) -> UIImage? {
get { cache.object(forKey: key as NSURL) }
set { newValue == nil ? cache.removeObject(forKey: key as NSURL) : cache.setObject(newValue!, forKey: key as NSURL) }
}
}
class ImageLoader: ObservableObject {
@Published var image: UIImage?
private(set) var isLoading = false
private let url: URL
private var cache: ImageCache?
private var cancellable: AnyCancellable?
private static let imageProcessingQueue = DispatchQueue(label: "image-processing")
init(url: URL, cache: ImageCache? = nil) {
self.url = url
self.cache = cache
}
deinit {
cancel()
}
func load() {
guard !isLoading else { return }
if let image = cache?[url] {
self.image = image
return
}
cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.handleEvents(receiveSubscription: { [weak self] _ in self?.onStart() },
receiveOutput: { [weak self] in self?.cache($0) },
receiveCompletion: { [weak self] _ in self?.onFinish() },
receiveCancel: { [weak self] in self?.onFinish() })
.subscribe(on: Self.imageProcessingQueue)
.receive(on: DispatchQueue.main)
.sink { [weak self] in self?.image = $0 }
}
func cancel() {
cancellable?.cancel()
}
private func onStart() {
isLoading = true
}
private func onFinish() {
isLoading = false
}
private func cache(_ image: UIImage?) {
image.map { cache?[url] = $0 }
}
}
struct ImageCacheKey: EnvironmentKey {
static let defaultValue: ImageCache = TemporaryImageCache()
}
extension EnvironmentValues {
var imageCache: ImageCache {
get { self[ImageCacheKey.self] }
set { self[ImageCacheKey.self] = newValue }
}
}
Run Code Online (Sandbox Code Playgroud)
完毕!
原始源码:https : //github.com/V8tr/AsyncImage
Pra*_*tti 11
AsyncImageiOS 15+ 中具有动画事务、占位符和网络阶段状态!正如其他答案所涵盖的那样,AsyncImage这是实现此目的的推荐方法SwiftUI,但新的配置View比此处显示的标准配置功能更强大:
AsyncImage(url: URL(string: "https://your_image_url_address"))
Run Code Online (Sandbox Code Playgroud)
AsyncImage从 URL 下载图像,无需URLSession样板。然而,苹果建议在等待最佳用户体验时使用占位符,而不是简单地下载图像并在加载时不显示任何内容。哦,我们还可以显示错误状态的自定义视图,并添加动画以进一步改进阶段转换。:D
我们可以使用添加动画transaction:并更改状态之间的基础Image属性。占位符可以具有不同的外观模式、图像或不同的修饰符。例如.resizable。
这是一个例子:
AsyncImage(
url: "https://dogecoin.com/assets/img/doge.png",
transaction: .init(animation: .easeInOut),
content: { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
}, placeholder: {
Color.gray
})
.frame(width: 500, height: 500)
.mask(RoundedRectangle(cornerRadius: 16)
Run Code Online (Sandbox Code Playgroud)
要在请求失败、成功、未知或正在进行时显示不同的视图,我们可以使用阶段处理程序。这会动态更新视图,类似于URLSessionDelegate处理程序。使用参数中的 SwiftUI 语法在状态之间自动应用动画。
AsyncImage(url: url, transaction: .init(animation: .spring())) { phase in
switch phase {
case .empty:
randomPlaceholderColor()
.opacity(0.2)
.transition(.opacity.combined(with: .scale))
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.transition(.opacity.combined(with: .scale))
case .failure(let error):
ErrorView(error)
@unknown default:
ErrorView()
}
}
.frame(width: 400, height: 266)
.mask(RoundedRectangle(cornerRadius: 16))
Run Code Online (Sandbox Code Playgroud)
我们不应该AsyncImage在所有需要从 URL 加载图像的情况下使用它。相反,当需要根据请求下载图像时,最好使用.refreshable或.task修饰符。请谨慎使用AsyncImage,因为每次状态更改都会重新下载图像View(简化请求)。在这里,Apple 建议await防止阻塞主线程 0(Swift 5.5+)。
Med*_*dhi 11
使用 loader 的 iOS 15+ 示例:
AsyncImage(
url: URL(string: "https://XXX"),
content: { image in
image.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 200, maxHeight: 100)
},
placeholder: {
ProgressView()
}
)
Run Code Online (Sandbox Code Playgroud)
对于 iOS 13、14(之前AsyncImage)并使用最新的属性包装器(无需使用PassthroughSubject<Data, Never>()
主视图
import Foundation
import SwiftUI
import Combine
struct TransactionCardRow: View {
var transaction: Transaction
var body: some View {
CustomImageView(urlString: "https://stackoverflow.design/assets/img/logos/so/logo-stackoverflow.png") // This is where you extract urlString from Model ( transaction.imageUrl)
}
}
Run Code Online (Sandbox Code Playgroud)
创建自定义ImageView
struct CustomImageView: View {
var urlString: String
@ObservedObject var imageLoader = ImageLoaderService()
@State var image: UIImage = UIImage()
var body: some View {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width:100, height:100)
.onReceive(imageLoader.$image) { image in
self.image = image
}
.onAppear {
imageLoader.loadImage(for: urlString)
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用发布者创建服务层以从 url 字符串下载图像
class ImageLoaderService: ObservableObject {
@Published var image: UIImage = UIImage()
func loadImage(for urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
self.image = UIImage(data: data) ?? UIImage()
}
}
task.resume()
}
}
Run Code Online (Sandbox Code Playgroud)
iOS 15中的新增功能,SwiftUI专门用于AsyncImage从互联网下载和显示远程图像。最简单的形式就是传递一个 URL,如下所示:
AsyncImage(url: URL(string: "https://www.thiscoolsite.com/img/nice.png"))
Run Code Online (Sandbox Code Playgroud)
您可以使用 KingFisher 和 SDWebImage
KingFisher https://github.com/onevcat/Kingfisher
var body: some View {
KFImage(URL(string: "https://example.com/image.png")!)
}
Run Code Online (Sandbox Code Playgroud)
SDWebImage https://github.com/SDWebImage/SDWebImageSwiftUI
WebImage(url: url)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
27486 次 |
| 最近记录: |