SwiftUI:基于 WebView 状态禁用/重新启用按钮

Posted

技术标签:

【中文标题】SwiftUI:基于 WebView 状态禁用/重新启用按钮【英文标题】:SwiftUI: Disable/Re-Enable button based on WebView state 【发布时间】:2021-02-16 22:22:19 【问题描述】:

我在WebView 周围有一个UIViewRepresentable 包装器。我在 webview 下方添加了一个带有前进和后退按钮的栏。我希望当WebViewcanGoBackcanGoForward 属性返回false 时禁用按钮,反之亦然。

ViewModel 包括:

class ViewModel: ObservableObject 
  ...
   @Published var canGoBackPublisher = CurrentValueSubject<Bool, Never>(false)
   @Published var canGoForwardPublisher = CurrentValueSubject<Bool, Never>(false)

ContentView 包括:

struct ContentView: View 
   @ObservedObject var viewModel = ViewModel()
   ...
   
   var body: some View 
      VStack(spacing: 0) 
         WebView(viewModel: viewModel).overlay (
            RoundedRectangle(cornerRadius: 4, style: .circular)
               .stroke(Color.gray, lineWidth: 0.5)
         )
         WebNavigationView(viewModel: viewModel)
             .frame(minWidth: 0, maxWidth: .infinity, minHeight: 64, maxHeight: 64)
             .background(Color.white)
      
      
   

WebNavigationView(按钮栏)包括:

struct WebNavigationView: View 
   @ObservedObject var viewModel: ViewModel
   ...      
   
   var body: some View 
      HStack(alignment: .center, spacing: 64, content: 
         Button(action: goBack) 
            Image(systemName: "chevron.left").resizable().aspectRatio(contentMode: .fit)
            .disabled(!viewModel.canGoBackPublisher.value).
                frame(width: 24, height: 24, alignment: .center).padding(.leading, 32)
         Button(action: goForward) 
            Image(systemName: "chevron.right").resizable().aspectRatio(contentMode: .fit)
            .disabled(!viewModel.canGoForwardPublisher.value)
                .frame(width: 24, height: 24, alignment: .center)
         Spacer()
      )
   
   ...

WebViewdelegate 包括:

   func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) 
      parent.viewModel.canGoBackPublisher.send(webView.canGoBack)
      parent.viewModel.canGoForwardPublisher.send(webView.canGoForward)
   

按钮按预期显示为灰色并禁用。但是它们不会对状态变化做出反应,即使viewModel.canGoBackPublisher.value 返回true,它们也会保持禁用状态。我是一名资深的 ios 开发人员,但对 SwiftUI 非常、非常、非常陌生

【问题讨论】:

【参考方案1】:

通过将发布者属性定义为@Published CurrentValueSubject,您最终会加倍发布者属性。

最简单的解决方法是将它们设为Published,它会为您处理大部分工作:

class ViewModel: ObservableObject 
   @Published var canGoBack = false
   @Published var canGoForward = false


//...
//In delegate:
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) 
  viewModel.canGoBack = webView.canGoBack
  viewModel.canGoForward = webView.canGoForward


//...
//In Navigation view:
Button(action: goBack) 
  Image(systemName: "chevron.left").resizable().aspectRatio(contentMode: .fit)
.disabled(!viewModel.canGoBack) //<-- here
.frame(width: 24, height: 24, alignment: .center).padding(.leading, 32)

Button(action: goForward) 
  Image(systemName: "chevron.right").resizable().aspectRatio(contentMode: .fit)
.disabled(!viewModel.canGoForward) //<-- here
.frame(width: 24, height: 24, alignment: .center)

如果需要,您仍然可以将它们定义为 CurrentValueSubject(并放弃 @Published 属性包装器),但在这种情况下可能不需要。

关于@Published 和 CurrentValueSubject 之间区别的好问题:Difference between CurrentValueSubject and @Published

【讨论】:

非常感谢!我认为迷失在太多的 SO 问题和中等文章中。非常感谢!

以上是关于SwiftUI:基于 WebView 状态禁用/重新启用按钮的主要内容,如果未能解决你的问题,请参考以下文章

根据 SwiftUI 中的状态(正常、突出显示、禁用)更改按钮颜色?

如何在 SwiftUI 中禁用子视图上的特定状态更改动画

SwiftUI 中的 WKWebView - 用户与网站交互时如何切换视图?

SwiftUI - 在禁用的 SecureField 中检测水龙头?

基于 VIewModel 状态更新 SwiftUI 视图?

iOS/iPadOS 中的 SwiftUI + DocumentGroup:如何重命名当前打开的文档