我有一个视图,可以将其显示为模式视图,也可以将其简单地推到导航堆栈上。按下它时,它在左上角有一个后退按钮,当它显示为模式时,我想添加一个关闭按钮(许多测试人员不容易发现他们可以向下滑动模式,实际上应该有一个明确的关闭按钮)。
现在,我有多个问题。
presentationMode
@Environment
变量无济于事,因为它的isPresented
值对于推送屏幕也适用。我当然可以自己传递一个isModal
变量,但这似乎很奇怪,这是唯一的方法吗?nil
,则即使默认的后退按钮也被隐藏。要复制并粘贴到Xcode中并使用的代码:
import SwiftUI
struct ContentView: View {
@State private var showModal = false
var body: some View {
NavigationView {
VStack(spacing: 20) {
Button("Open modally") {
self.showModal = true
}
NavigationLink("Push", destination: DetailView(isModal: false))
}
.navigationBarTitle("Home")
}
.sheet(isPresented: $showModal) {
NavigationView {
DetailView(isModal: true)
}
}
}
}
struct DetailView: View {
@Environment(\.presentationMode) private var presentationMode
let isModal: Bool
var body: some View {
Text("Hello World")
.navigationBarTitle(Text("Detail"), displayMode: .inline)
.navigationBarItems(leading: closeButton, trailing: deleteButton)
}
private var closeButton: some View {
Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
Image(systemName: "xmark")
.frame(height: 36)
}
}
private var deleteButton: some View {
Button(action: { print("DELETE") }) {
Image(systemName: "trash")
.frame(height: 36)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如果我更改closeButton
以返回可选的AnyView?
,然后在nil
为假时返回isModal
,则根本没有返回按钮。我也不能两次调用navigationBarItems
,一次是使用前导按钮,还是是使用尾随按钮,因为后一个调用会覆盖第一个调用。我有点卡在这里。
答案 0 :(得分:1)
好的,我做到了。它不是很漂亮,我很愿意接受不同的建议,但是它很有效
import SwiftUI
extension View {
func eraseToAnyView() -> AnyView {
AnyView(self)
}
public func conditionalNavigationBarItems(_ condition: Bool, leading: AnyView, trailing: AnyView) -> some View {
Group {
if condition {
self.navigationBarItems(leading: leading, trailing: trailing)
} else {
self
}
}
}
}
struct ContentView: View {
@State private var showModal = false
var body: some View {
NavigationView {
VStack(spacing: 20) {
Button("Open modally") {
self.showModal = true
}
NavigationLink("Push", destination: DetailView(isModal: false))
}
.navigationBarTitle("Home")
}
.sheet(isPresented: $showModal) {
NavigationView {
DetailView(isModal: true)
}
}
}
}
struct DetailView: View {
@Environment(\.presentationMode) private var presentationMode
let isModal: Bool
var body: some View {
Text("Hello World")
.navigationBarTitle(Text("Detail"), displayMode: .inline)
.navigationBarItems(trailing: deleteButton)
.conditionalNavigationBarItems(isModal, leading: closeButton, trailing: deleteButton)
}
private var closeButton: AnyView {
Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
Image(systemName: "xmark")
.frame(height: 36)
}.eraseToAnyView()
}
private var deleteButton: AnyView {
Button(action: { print("DELETE") }) {
Image(systemName: "trash")
.frame(height: 36)
}.eraseToAnyView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
答案 1 :(得分:1)
我看不到任何麻烦,只需将“关闭”按钮添加到导航栏中。您只需要重新排列View层次结构,无需将任何绑定传递给DetailView
import SwiftUI
struct DetailView: View {
var body: some View {
Text("Detail View")
}
}
struct ContentView: View {
@State var sheet = false
var body: some View {
NavigationView {
VStack(spacing: 20) {
Button("Open modally") {
self.sheet = true
}
NavigationLink("Push", destination: DetailView())
}.navigationBarTitle("Home")
}
.sheet(isPresented: $sheet) {
NavigationView {
DetailView().navigationBarTitle("Title").navigationBarItems(leading: Button(action: {
self.sheet.toggle()
}, label: {
Text("Dismiss")
}))
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
您仍然可以通过向下滑动来消除它,可以添加一些按钮(作为DetailView声明的一部分)...等等。
按下后,您将拥有默认的后退按钮;如果显示模态,则将您关闭 按钮。
更新(基于讨论)
.sheet(isPresented: $sheet) {
NavigationView {
GeometryReader { proxy in
DetailView().navigationBarTitle("Title")
.navigationBarItems(leading:
HStack {
Button(action: {
self.sheet.toggle()
}, label: {
Text("Dismiss").padding(.horizontal)
})
Color.clear
Button(action: {
}, label: {
Image(systemName: "trash")
.imageScale(.large)
.padding(.horizontal)
})
}.frame(width: proxy.size.width)
)
}
}
}
最终我建议您使用
extension View {
@available(watchOS, unavailable)
public func navigationBarItems<L, T>(leading: L?, trailing: T) -> some View where L : View, T : View {
Group {
if leading != nil {
self.navigationBarItems(leading: leading!, trailing: trailing)
} else {
self.navigationBarItems(trailing: trailing)
}
}
}
}
答案 2 :(得分:0)
只要我们提供.navigationBarItems(leading: _anything_)
(即任何内容),标准后退按钮就消失了,因此您必须有条件地提供自己的后退按钮。
以下方法有效(已通过Xcode 11.2 / iOS 13.2测试)
.navigationBarItems(leading: Group {
if isModal {
closeButton
} else {
// custom back button here calling same dismiss
}
}, trailing: deleteButton)
更新:替代方法可能如下(已在同一测试中)
var body: some View {
VStack {
if isModal {
Text("Hello")
.navigationBarItems(leading: closeButton, trailing: deleteButton)
} else {
Text("Hello")
.navigationBarItems(trailing: deleteButton)
}
}
.navigationBarTitle("Test", displayMode: .inline)
}