Swift 迅捷可以';无法从下载url获取图像

Swift 迅捷可以';无法从下载url获取图像,swift,swiftui,Swift,Swiftui,我有以下代码从下载url加载图像并将其显示为UIImage 我希望它能工作,但不知何故,只显示占位符图像'ccc',而不是下载url中的实际图像。怎么会这样 我的URL是从数据库中提取的,有点像这样: 您的图像是一个普通的旧var,在构建视图时恰好为零。SwiftUI只会根据@ObservedObject、@State或@Binding中的更改而重建自身,因此,将图像移动到图像加载器上的@Published属性,它就会工作。以下是我的缓存图像视图: import SwiftUI import

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

我希望它能工作,但不知何故,只显示占位符图像
'ccc'
,而不是下载url中的实际图像。怎么会这样

我的URL是从数据库中提取的,有点像这样:


您的图像是一个普通的旧
var
,在构建视图时恰好为零。SwiftUI只会根据
@ObservedObject
@State
@Binding
中的更改而重建自身,因此,将图像移动到
图像加载器上的
@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)
  }
}
导入快捷界面
进口联合收割机
导入UIKit
类ImageCache{
枚举错误:Swift.Error{
案例数据转换失败
案件审理人错误(Swift.错误)
}
静态let shared=ImageCache()
private let cache=NSCache()
私有init(){}
静态func图像(用于url:url?->AnyPublisher{
guard let url=url else{
返回空()
}
guard let image=shared.cache.object(forKey:url作为NSURL)else{
返回URL会话
.共享
.dataTaskPublisher(用于:url)
.tryMap{(元组)->UIImage in
let(data,u)=元组
guard let image=UIImage(数据:数据)else{
抛出错误。DataConversion失败
}
shared.cache.setObject(图像,forKey:url为NSURL)
返回图像
}
.mapError({error中的error.sessionError(error)})
.删除任何发布者()
}
返回刚才(图片)
.mapError({inFatalError()})
.删除任何发布者()
}
}
类ImageModel:ObservableObject{
@已发布的var映像:UIImage?=nil
var cacheSubscription:是否可以取消?
初始化(url:url?){
cacheSubscription=ImageCache
.image(用于:url)
.replaceError(带:nil)
.receive(在:RunLoop.main上,选项:。无)
.assign(到:\.image,在:self上)
}
}
结构远程映像:视图{
@观测对象var图像模型:图像模型
私有let contentMode:contentMode
初始化(url:url?,contentMode:contentMode=.fit){
imageModel=imageModel(url:url)
self.contentMode=contentMode
}
var body:一些观点{
图像模型
.形象
.map{Image(uiImage:$0).resizeable().aspectRatio(contentMode:contentMode)}
??图像(系统名称:“问号”).resizeable().aspectRatio(内容模式:内容模式)
}
}

你是什么意思?我的数据变量已经发布了
,或者您讨论过另一个变量吗?您的视图是`var image:UIImage'的函数?{imageLoader.data.flatMap(UIImage.init)}`这不是一个属性包装器,所以SwiftUI不知道您的计算属性何时更改,视图永远无法重建
@Published var image:UIImage?{..
将返回`imageLoader.data.flatMap(UIImage.init)`并且
@Published仅在类的属性上可用
-如何?请看我的示例:@Published属性在一个observeObject上,视图观察它。
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()
    }
}
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)
  }
}