SwiftUI 无法从下载地址获取图片

Posted

技术标签:

【中文标题】SwiftUI 无法从下载地址获取图片【英文标题】:SwiftUI can't get image from download url 【发布时间】:2020-01-14 18:26:38 【问题描述】:

我有以下代码从下载网址加载图像并将其显示为 UIImage。

我希望它可以工作,但不知何故,仅显示占位符图像'ccc',而不是下载网址中的实际图像。怎么会这样?

我的网址是从数据库中获取的,看起来像这样:

https://firebasestorage.googleapis.com/v0/b/.../o/P...alt=media&token=...-579f...da

struct ShelterView: View 
    var title: String
    var background: String
    var available: Bool
    var distance: Double
    var gender: String

    @ObservedObject private var imageLoader: Loader

    init(title: String, background: String, available: Bool, distance: Double, gender: String) 
        self.title = title
        self.background = background
        self.available = available
        self.distance = distance
        self.gender = gender
        self.imageLoader = Loader(background)
    

    var image: UIImage? 
        imageLoader.data.flatMap(UIImage.init)
    

    var body: some View 
        VStack 
            VStack(alignment: .leading) 
                VStack(alignment: .leading, spacing: 0) 
                    Text(title)
                        .font(Font.custom("Helvetica Now Display Bold", size: 30))
                        .foregroundColor(.white)
                        .padding(15)
                        .lineLimit(2)
                    HStack(spacing: 25) 
                        IconInfo(image: "bed.double.fill", text: String(available), color: .white)
                        if gender != "" 
                            IconInfo(image: "person.fill", text: gender, color: .white)
                        
                    
                    .padding(.leading, 15)
                
                Spacer()
                IconInfo(image: "mappin.circle.fill", text: String(distance) + " miles away", color: .white)
                    .padding(15)
            
            Spacer()
        
        .background(
            Image(uiImage: image ?? UIImage(named: "ccc")!) <-- HERE
                .brightness(-0.11)
                .frame(width: 255, height: 360)
        )
            .frame(width: 255, height: 360)
            .cornerRadius(30)
            .shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
    

final class Loader: ObservableObject 

    var task: URLSessionDataTask!
    @Published var data: Data? = nil

    init(_ urlString: String) 
        print(urlString)
        let url = URL(string: urlString)
        task = URLSession.shared.dataTask(with: url!, completionHandler:  data, _, _ in
            DispatchQueue.main.async 
                self.data = data
            
        )
        task.resume()
    
    deinit 
        task.cancel()
    

【问题讨论】:

【参考方案1】:

您的图像是一个普通的旧var,在构建视图时恰好为零。 SwiftUI 只会重建自身以响应@ObservedObject@State@Binding 的变化,因此将您的图像移动到imageLoader 上的@Published 属性,它将起作用。这是我的缓存图像视图:

import SwiftUI
import Combine
import UIKit

class ImageCache 
  enum Error: Swift.Error 
    case dataConversionFailed
    case sessionError(Swift.Error)
  
  static let shared = ImageCache()
  private let cache = NSCache<NSURL, UIImage>()
  private init()  
  static func image(for url: URL?) -> AnyPublisher<UIImage?, ImageCache.Error> 
    guard let url = url else 
      return Empty().eraseToAnyPublisher()
    
    guard let image = shared.cache.object(forKey: url as NSURL) else 
      return URLSession
        .shared
        .dataTaskPublisher(for: url)
        .tryMap  (tuple) -> UIImage in
          let (data, _) = tuple
          guard let image = UIImage(data: data) else 
            throw Error.dataConversionFailed
          
          shared.cache.setObject(image, forKey: url as NSURL)
          return image
      
      .mapError( error in Error.sessionError(error) )
      .eraseToAnyPublisher()
    
    return Just(image)
      .mapError( _ in fatalError() )
      .eraseToAnyPublisher()
  


class ImageModel: ObservableObject 
  @Published var image: UIImage? = nil
  var cacheSubscription: AnyCancellable?
  init(url: URL?) 
    cacheSubscription = ImageCache
      .image(for: url)
      .replaceError(with: nil)
      .receive(on: RunLoop.main, options: .none)
      .assign(to: \.image, on: self)
  


struct RemoteImage : View 
  @ObservedObject var imageModel: ImageModel
  private let contentMode: ContentMode
  init(url: URL?, contentMode: ContentMode = .fit) 
    imageModel = ImageModel(url: url)
    self.contentMode = contentMode
  
  var body: some View 
    imageModel
      .image
      .map  Image(uiImage:$0).resizable().aspectRatio(contentMode: contentMode) 
      ?? Image(systemName: "questionmark").resizable().aspectRatio(contentMode: contentMode)
  

【讨论】:

var 是什么意思?我的数据 var 已经是 @Published,还是你说的是另一个 var? 你的视图是`var image: UIImage的函数? imageLoader.data.flatMap(UIImage.init) ` 这不是一个属性包装器,因此 SwiftUI 不知道您的计算属性何时更改并且视图永远不会重建 @Published var image: UIImage? .. 将返回`imageLoader.data.flatMap(UIImage.init)` 和@Published is only available on properties of classes- 怎么会这样? 看我的例子:@published 属性在 ObservableObject 上,View 观察它。

以上是关于SwiftUI 无法从下载地址获取图片的主要内容,如果未能解决你的问题,请参考以下文章

关于Java/Kotlin下载图片,图片打开不能显示问题探究

怎样批量获取网页中的所有图片地址?求大神帮助

s3中图片打包后获取包地址

前端实现获取canvas下载地址以及下载basse64图片格式功能

前端实现获取canvas下载地址以及下载basse64图片格式功能

Android从Uri获取视频图片的真实地址