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 不适合与LPLinkView
s 一起使用。考虑使用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
中使用LPLinkView
s 会导致大量内存泄漏。最好的办法是使用嵌入在 ScrollView
中的 VStack
。
ScrollView
VStack
ForEach(links, id: \.self) link in
if let url = URL(string: link)
LinkRow(url: url)
.padding()
这将使LPLinkView
s 在加载时自行调整大小。
我已经在一个应用程序中完成了这项工作,与使用 List
相比,它有了显着的改进。然而有一点需要注意的是,如果用户在预览仍在加载的同时很快在屏幕上显示上下滚动,则可能会导致随机崩溃。不幸的是,我还没有找到解决方案。我认为所有这些崩溃的发生都是因为LPMetadataProvider
要求您在主线程上被调用,显然这不能很好地平滑滚动。
【讨论】:
你可以用LazyVStack
代替VStack
以上是关于LinkPresentation 视图未在 SwiftUI 中完全加载的主要内容,如果未能解决你的问题,请参考以下文章
Xcode 13 dyld:库未加载:/System/Library/Frameworks/LinkPresentation.framework/LinkPresentation