为了优化,您可以考虑使用视图回收机制(“视图池”),以便在每次显示新视频时重用web视图实例,而不是创建新实例。
然而,由于您特别询问当用户超过2个视频时如何销毁网络视图,因此您可以实现手动取消分配这些网络视图并清除其内容的逻辑。
手动销毁
WKWebView
,您需要:
-
从其超级视图中删除web视图(如果有)。
-
设置
navigationDelegate
和
UIDelegate
到
nil
。
-
致电
stopLoading
方法。
-
将web视图本身设置为
无
(如果web视图没有强引用,则通常由ARC处理)。
首先,在中添加标志
SmartReelView
要检查web视图是否处于活动状态,请执行以下操作:
@Binding var isActive: Bool
更新
updateUIView
和
makeUIView
考虑活动状态的方法:
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
// existing code
if isActive {
loadInitialContent(in: webView)
}
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
if isActive {
// existing code
} else {
destroyWebView(uiView)
}
}
private func destroyWebView(_ webView: WKWebView) {
webView.navigationDelegate = nil
webView.stopLoading()
webView.removeFromSuperview()
}
然后,在
SingleVideoView
,引入逻辑以更新
isActive
基于用户滚动的距离的绑定。您可能需要传递索引并计算视图是否在当前活动视频的2个视频内:
struct SingleVideoView: View {
// existing properties
@Binding var activeIndex: Int // The index of the currently active (visible) video
let index: Int // The index of this particular video
var body: some View {
// existing code
SmartReelView(link: link, isPlaying: $isVideoPlaying, viewIsShowing: $viewIsShowing, isActive: .constant(shouldActivate))
}
private var shouldActivate: Bool {
return abs(activeIndex - index) <= 2
}
}
在里面
AllVideoView
,保持当前活动视频索引的状态:
@State private var activeIndex = 0
将此索引传递给每个
单个视频视图
:
ForEach(arr.indices, id: \.self) { index in
SingleVideoView(link: arr[index], activeIndex: $activeIndex, index: index)
}
最后,更新
activeIndex
每当
TabView
的选择更改。
这些更改应将内存中的网络视图数量限制为仅在当前活动视频的2个视频内的视图,这应能缓解资源问题。
在SingleVideoView中更改isActive的值并不总是触发updateUiView,尤其是当视图不在屏幕上时。如果我在视图的onChange中放入一个打印,那么每当视频有2个索引时,就会运行这个打印。但是在destroyWebView中添加打印并不总是运行的。这意味着
updateUiView
如果视图未显示,func可能不会检查更改。如果我滚动两个视频,然后滚动回视频,则会打印“已删除”。
有没有办法从SingleVideoView中保存实例SmartReelView,并直接调用destroyWebView以确保其运行?
updateUI视图
没有被一致调用应该是由于SwiftUI的优化;它不会更新当前不在屏幕上的视图。由于您正在使用
选项卡视图
,SwiftUI试图通过不更新不可见的选项卡来提高效率。
直接持有SwiftUI
View
由于SwiftUI的声明性,不建议使用。由于操作SwiftUI的托管状态(
@Published
,
@State
)在的更新周期内
UIViewRepresentable
可能导致未定义的行为或错误,另一种方法是封装
WK Web视图
SwiftUI可以观察到的专用类中的管理逻辑。这避免了改变
@已发布
或
@州
更新周期内的变量。
创建一个可以观察到的专用WebViewManager类:
class WebViewManager: ObservableObject {
var webView: WKWebView?
var link: String
init(link: String) {
self.link = link
createWebView()
}
func createWebView() {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.allowsInlineMediaPlayback = true
self.webView = WKWebView(frame: .zero, configuration: webConfiguration)
// additional setup logic here
}
func destroyWebView() {
self.webView?.loadHTMLString("", baseURL: nil)
self.webView = nil
}
}
修改
SmartReelView
和
单个视频视图
使用此
WebViewManager
:
struct SmartReelView: UIViewRepresentable {
@ObservedObject var webViewManager: WebViewManager
func makeUIView(context: Context) -> WKWebView {
return webViewManager.webView ?? WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
if webViewManager.webView == nil {
webViewManager.createWebView()
}
// additional logic to load or reload the content
}
}
struct SingleVideoView: View {
let link: String
@State private var isActive = true
@ObservedObject var webViewManager: WebViewManager
var body: some View {
SmartReelView(webViewManager: webViewManager)
.onAppear {
isActive = true
if webViewManager.webView == nil {
webViewManager.createWebView()
}
}
.onDisappear {
isActive = false
webViewManager.destroyWebView()
}
}
}
修改
ForEach
循环以创建
WebViewManager
每个
单个视频视图
:
ForEach(arr, id: \.self) { id in
SingleVideoView(link: id, webViewManager: WebViewManager(link: id))
.tag(id)
}
这样
WebViewManager
处理的创建和销毁
WK Web视图
实例。这个
SmartReelView
和
单个视频视图
观察该管理器并做出相应的反应,而无需直接修改
@州
或
@已发布
更新周期内的变量。
我仍然会考虑
WK Web视图
实例,包括维护一个可重用视图的集合,在需要时分发它们,并在不再使用时将它们返回到池中。
一个简化的示例(重点关注WebView池)将包括第一个
WebViewPool
经理
该管理器将处理池化的逻辑:
class WebViewPool {
private var pool: [WKWebView] = []
func getWebView() -> WKWebView {
if let webView = pool.first {
pool.removeFirst()
return webView
} else {
// Create a new web view, configure it as needed
let webView = WKWebView()
return webView
}
}
func returnWebView(_ webView: WKWebView) {
// Optionally clear the webView content
webView.loadHTMLString("", baseURL: nil)
pool.append(webView)
}
}
然后,您可以在需要web视图的SwiftUI视图中创建此管理器的实例,例如
所有视频视图
。
struct AllVideoView: View {
@State private var webViewPool = WebViewPool()
//... existing code
}
在
单个视频视图
或
SmartReelView
,您可以使用池在视图出现时获取web视图,并在视图消失时返回该视图。
struct SingleVideoView: View {
let link: String
@Binding var webViewPool: WebViewPool
//... existing code
var body: some View {
// existing code
SmartReelView(link: link, webViewPool: $webViewPool)
.onAppear {
// Check out a WebView when appearing
}
.onDisappear {
// Return WebView when disappearing
}
}
}
struct SmartReelView: UIViewRepresentable {
let link: String
@Binding var webViewPool: WebViewPool
var webView: WKWebView?
func makeUIView(context: Context) -> WKWebView {
webView = webViewPool.getWebView()
// existing code
return webView!
}
func updateUIView(_ uiView: WKWebView, context: Context) {
// existing code
}
func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) {
webViewPool.returnWebView(uiView)
}
}
这并不涵盖所有边缘案例。管理web视图的生命周期(签出和返回)需要更加细致。根据您的需要,您不仅可以在视图出现时,还可以在需要加载新视频时签出网络视图。
尽管如此,这个想法仍然存在:通过这种方式重用web视图,您可以最大限度地减少创建和销毁web视图实例的开销,这将提高应用程序的性能。