如何使用LinkPresentation在SwiftUI中获取Link元数据?

7jmck4yq  于 2023-09-30  发布在  Swift
关注(0)|答案(2)|浏览(98)

根据WWDC20和其他文章,从给定的URL中获取图像URL似乎很容易。下面是我的启动代码。这只是列出了一个随机的url列表,并应该为url的丰富链接预览获取imageurl。只需使用LPMetadataProvider获取元数据。但我不能让它显示图像的URL。有人知道SwiftUI是怎么做的吗?

import SwiftUI
import LinkPresentation

struct ExampleHTTPLinks: View {
    var links = [ "https://www.google.com", "https://www.hotmail.com"]
    let metadataProvider = LPMetadataProvider()
    
    
    var body: some View {
        
        
        List(links, id:\.self) { item in
            HStack {
                Text(item)
                Image(systemName: "heart.fill")
                metadataProvider.startFetchingMetadata(for: URL(string: item)!) { metadata, error in
                  if error != nil {
                    // The fetch failed; handle the error.
                    // Examples: server doesn't respond, is too slow, user doesn't have network.
                    return
                  }
                 let linkView = LPLinkView(metadata: metadata)
                    Image(linkView.image)
                  // Make use of the fetched metadata.
                }
            }
        }
    }
}

struct ExampleHTTPLinks_Previews: PreviewProvider {
    static var previews: some View {
        ExampleHTTPLinks()
    }
}
gzszwxb4

gzszwxb41#

下面是一个工作版本:

class LinkViewModel : ObservableObject {
    let metadataProvider = LPMetadataProvider()
    
    @Published var metadata: LPLinkMetadata?
    @Published var image: UIImage?
    
    init(link : String) {
        guard let url = URL(string: link) else {
            return
        }
        metadataProvider.startFetchingMetadata(for: url) { (metadata, error) in
            guard error == nil else {
                assertionFailure("Error")
                return
            }
            DispatchQueue.main.async {
                self.metadata = metadata
            }
            guard let imageProvider = metadata?.imageProvider else { return }
            imageProvider.loadObject(ofClass: UIImage.self) { (image, error) in
                guard error == nil else {
                    // handle error
                    return
                }
                if let image = image as? UIImage {
                    // do something with image
                    DispatchQueue.main.async {
                        self.image = image
                    }
                } else {
                    print("no image available")
                }
            }
        }
    }
}

struct MetadataView : View {
    @StateObject var vm : LinkViewModel
    
    var body: some View {
        VStack {
            if let metadata = vm.metadata {
                Text(metadata.title ?? "")
            }
            if let uiImage = vm.image {
                Image(uiImage: uiImage)
                    .resizable()
                    .frame(width: 100, height: 100)
            }
        }
    }
}

struct ContentView: View {
    var links = [ "https://www.google.com", "https://www.hotmail.com"]
    let metadataProvider = LPMetadataProvider()
    
    var body: some View {
        List(links, id:\.self) { item in
            HStack {
                Text(item)
                Image(systemName: "heart.fill")
                MetadataView(vm: LinkViewModel(link: item))
            }
        }
    }
}

如果您尝试将LPMetadataProvider用于多个调用,它会发出抱怨,因此我将它移到了视图模型中。
该图像由NSImageProvider出售--您可以看到loadObject调用是从中获取UIImage的。
请注意,如果您想使用Apple提供的开箱即用的演示文稿,则可以使用LPLinkView。因为它是一个UIView,要在SwiftUI中使用它,你必须用UIViewRepresentable Package 它:

struct LPLinkViewRepresented: UIViewRepresentable {
    var metadata: LPLinkMetadata
    
    func makeUIView(context: Context) -> LPLinkView {
        return LPLinkView(metadata: metadata)
    }
    
    func updateUIView(_ uiView: LPLinkView, context: Context) {
        
    }
}

struct MetadataView : View {
    @StateObject var vm : LinkViewModel
    
    var body: some View {
        if let metadata = vm.metadata {
            LPLinkViewRepresented(metadata: metadata)
        } else {
            EmptyView()
        }
    }
}

class LinkViewModel : ObservableObject {
    let metadataProvider = LPMetadataProvider()
    
    @Published var metadata: LPLinkMetadata?
    
    init(link : String) {
        guard let url = URL(string: link) else {
            return
        }
        metadataProvider.startFetchingMetadata(for: url) { (metadata, error) in
            guard error == nil else {
                assertionFailure("Error")
                return
            }
            DispatchQueue.main.async {
                self.metadata = metadata
            }
        }
    }
}
flseospp

flseospp2#

接受的答案做的工作。这是一个改进的版本,具有缓存和Swift并发性。

import LinkPresentation
import SwiftUI

struct URLPreview<Content: View>: View {
  let url: URL
  let fallback: () -> Content

  @State var metadata: LPLinkMetadata? = nil

  var body: some View {
    if let metadata = metadata {
      LinkPresentationView(metadata: metadata)
    } else {
      fallback()
        .onAppear(perform: updateMetadata)
    }
  }

  func updateMetadata() {
    Task {
      metadata = await URLPreviewCache.metadata(for: url)
    }
  }
}

private enum URLPreviewCache {
  private static var cache: Cache<URL, LPLinkMetadata> = .init(totalCountLimit: 500)

  static func metadata(for url: URL) async -> LPLinkMetadata? {
    if let metadata = cache[url] {
      return metadata
    } else {
      return await fetch(for: url)
    }
  }

  static func preHeat(_ urls: [URL]) {
    for url in urls {
      Task {
        await fetch(for: url)
      }
    }
  }

  private static func fetch(for url: URL) async -> LPLinkMetadata? {
    do {
      let provider = LPMetadataProvider()
      let metadata = try await provider.startFetchingMetadata(for: url)
      cache[url] = metadata
      return metadata
    } catch {
      log(error)
      return nil
    }
  }
}

private struct LinkPresentationView: NSViewRepresentable {
  let metadata: LPLinkMetadata

  func makeNSView(context: Context) -> LPLinkView {
    LPLinkView(metadata: metadata)
  }

  func updateNSView(_ nsView: LPLinkView, context: Context) {}
}

如果您的目标不是macOS,请将NS替换为UI

相关问题