SwiftUI:Disable/Re-Enable 基于 WebView 状态的按钮
SwiftUI: Disable/Re-Enable button based on WebView state
我有一个 UIViewRepresentable
包装 WebView
。我在 webview 下方添加了一个带有前进和后退按钮的栏。我希望在 WebView
的 canGoBack
和 canGoForward
属性 return 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()
})
}
...
WebView
的 delegate
包括:
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
parent.viewModel.canGoBackPublisher.send(webView.canGoBack)
parent.viewModel.canGoForwardPublisher.send(webView.canGoForward)
}
按钮开始变灰并按预期禁用。但它们不会对状态变化做出反应,即使在 viewModel.canGoBackPublisher.value
returns true
时它们也会保持禁用状态。我是一名长期的 iOS 开发人员,但对 SwiftUI 非常、非常、非常陌生
您最终将发布者 属性 定义为 @Published and 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 之间区别的好问题:
我有一个 UIViewRepresentable
包装 WebView
。我在 webview 下方添加了一个带有前进和后退按钮的栏。我希望在 WebView
的 canGoBack
和 canGoForward
属性 return 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()
})
}
...
WebView
的 delegate
包括:
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
parent.viewModel.canGoBackPublisher.send(webView.canGoBack)
parent.viewModel.canGoForwardPublisher.send(webView.canGoForward)
}
按钮开始变灰并按预期禁用。但它们不会对状态变化做出反应,即使在 viewModel.canGoBackPublisher.value
returns true
时它们也会保持禁用状态。我是一名长期的 iOS 开发人员,但对 SwiftUI 非常、非常、非常陌生
您最终将发布者 属性 定义为 @Published and 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 之间区别的好问题: