LinkPresentation 视图未在 SwiftUI 中完全加载

Posted

技术标签:

【中文标题】LinkPresentation 视图未在 SwiftUI 中完全加载【英文标题】:LinkPresentation views not fully loading in 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。我在下面发布了详细的答案。 【参考方案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://***.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

以上是关于LinkPresentation 视图未在 SwiftUI 中完全加载的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 13 dyld:库未加载:/System/Library/Frameworks/LinkPresentation.framework/LinkPresentation

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

未在详细视图中加载数据

ListBoxFor 未在提交时完全更新视图模型

MVC 部分视图已更新,但未在主视图中显示

刀片未在视图上显示消息