【问题标题】:LinkPresentation views not fully loading in SwiftUILinkPresentation 视图未在 SwiftUI 中完全加载
【发布时间】:2019-09-22 09:14:48
【问题描述】:

我正在使用从一篇很棒的文章 here 中找到的代码,该文章演示了如何在 SwiftUI 中使用 LinkPresentation 框架。

但是我遇到了一个我无法找到解决方案的小问题 - 链接预览会加载其元数据,但一旦完全加载就不会刷新视图,除非我执行了强制刷新视图的操作,例如旋转电话。

它们的加载量与此一样多:

然后旋转后是这样的:

我希望在加载元数据后完全刷新视图。我觉得我可能需要在某个地方添加一些绑定,但我不知道在哪里。任何人都可以提供帮助吗?

这是 UIViewRepresentable

import SwiftUI
import LinkPresentation

struct URLPreview : UIViewRepresentable {
    var previewURL:URL

    func makeUIView(context: Context) -> LPLinkView {
        LPLinkView(url: previewURL)
    }

    func updateUIView(_ view: LPLinkView, context: Context) {
        // New instance for each update

        let provider = LPMetadataProvider()

        provider.startFetchingMetadata(for: previewURL) { (metadata, error) in
            if let md = metadata {
                DispatchQueue.main.async {
                    view.metadata = md
                    view.sizeToFit()

                }
            }
        }
    }
}

这就是它的名称:

struct Content: View {
    var body: some View {
        URLPreview(previewURL: URL(string: "www.apple.com")!)
    }
}

【问题讨论】:

  • 如何在不循环数组的情况下加载表格列表?我需要实现您在屏幕截图中提到的相同内容,即从数组到表列表的多个 URL,但它只是分散
  • @NSPratik TableViews 不适合与LPLinkViews 一起使用。考虑使用VStack。我在下面发布了详细的答案。

标签: swift swiftui ios13


【解决方案1】:

触发重绘是您所需要的。不喜欢这个,但你可以尝试绑定一个状态 CGSize 并将框架设置为宽度/高度。

struct URLPreview : UIViewRepresentable {
    var previewURL:URL
    //Add binding
    @Binding var metaSize: CGSize

    func makeUIView(context: Context) -> LPLinkView {
        LPLinkView(url: previewURL)
    }

    func updateUIView(_ view: LPLinkView, context: Context) {
        // New instance for each update

        let provider = LPMetadataProvider()

        provider.startFetchingMetadata(for: previewURL) { (metadata, error) in
            if let md = metadata {
                DispatchQueue.main.async {
                    view.metadata = md
                    view.sizeToFit()
                    //Set binding after resize
                    self.metaSize = view.frame.size
                }
            }
        }
    }
}
struct ContentView: View {
    //can default original state
    @State var metaSize: CGSize = CGSize()
    
    var body: some View {
        URLPreview(previewURL: URL(string: "www.apple.com")!, metaSize: $metaSize)
            .frame(width: metaSize.width, height: metaSize.height)
    }
}

更新

NSPratik 是对的,该解决方案对于列表来说并不是真正可行的。所以修改后的解决方案其实就是用一个简单的 Bool State 来切换列表生成的 Views:

struct ContentView: View {
    //can default original state
    @State var togglePreview = false
    let urls: [String] = ["https://medium.com","https://apple.com","https://yahoo.com","https://stackoverflow.com"]
    
    var body: some View {
        List(urls, id: \.self) { url in
            URLPreview(previewURL: URL(string: url)!, togglePreview: self.$togglePreview)
                .aspectRatio(contentMode: .fit)
                .padding()
        }
    }
}

struct URLPreview : UIViewRepresentable {
    var previewURL:URL
    //Add binding
    @Binding var togglePreview: Bool

    func makeUIView(context: Context) -> LPLinkView {
        let view = LPLinkView(url: previewURL)
        
        let provider = LPMetadataProvider()

        provider.startFetchingMetadata(for: previewURL) { (metadata, error) in
            if let md = metadata {
                DispatchQueue.main.async {
                    view.metadata = md
                    view.sizeToFit()
                    self.togglePreview.toggle()
                }
            }
        }
        
        return view
    }
    
    func updateUIView(_ uiView: LPLinkView, context: UIViewRepresentableContext<URLPreview>) {
    }
}

我们只需使用togglePreview 作为触发器,将其传递给 UIView 中的 Binding var,然后设置我们的 List。即使这触发了 List 中的所有 View,也不会有任何动画来反映完全加载的 LinkView 的大小调整。

【讨论】:

  • 这是有效的......但我需要在列表视图中加载多个 URL。
  • @NSPratik 好点,我添加了一个可以提供帮助的新解决方案
  • 谢谢@DannyB 我会检查并回来
  • “即使这会触发所有视图...”如果前几个链接正在加载,同时,我向下滚动到第 20 个单元格。突然然后加载第一个单元格的链接,它会正确刷新第一个单元格还是会有任何可重用性问题?
  • 我已经尝试过,但性能并不流畅,并且存在单元可重用性问题。你能帮帮我吗?
【解决方案2】:

List 中使用LPLinkViews 会导致大量内存泄漏。最好的办法是使用嵌入在 ScrollView 中的 VStack

ScrollView {
    VStack {
        ForEach(links, id: \.self) { link in
            if let url = URL(string: link) {
                LinkRow(url: url)
            }
        }
    }
    .padding()
}

这将使LPLinkViews 在加载时自行调整大小。

我已经在一个应用程序中完成了这项工作,与使用 List 相比,它有了显着的改进。然而有一点需要注意的是,如果用户在预览仍在加载的同时很快在屏幕上显示上下滚动,则可能会导致随机崩溃。不幸的是,我还没有找到解决方案。我认为所有这些崩溃的发生都是因为LPMetadataProvider 要求您在主线程上被调用,显然这不能很好地平滑滚动。

【讨论】:

  • 你可以用LazyVStack代替VStack
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
相关资源
最近更新 更多