I am currently trying to figure out how to dismiss a sheet in SwiftUI. I created a view for the Dismiss-Button because I want to use that button multiple times at different locations in the app. When the sheet pops up, pressing the button is not doing anything, I can only dismiss the sheet via swiping down on the screen.
I first tried to follow a YouTube tutorial, but it uses the deprecated .presentationMode Therefore I am currently trying to make the .dismiss variable working, but I can't figure out what I am doing wrong. I already tried declaring .dismiss in the Button-View and Portfolio-View. I also used dismiss() and dismiss.callAsFunction().
Here is my Button-View (XMarkButton), the presented Sheet-View (PortfolioView) and my Home-View, where the sheet will be displayed:
XMarkButton
struct XMarkButton: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button(action: {
dismiss.callAsFunction()
}, label: {
Image(systemName: "xmark")
.font(.headline)
})
}
}
PortfolioView
struct PortfolioView: View {
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
Text("Portfolio")
}
}
.navigationTitle("Edit Portfolio")
.toolbar(content: {
ToolbarItem(placement: .navigationBarLeading) {
XMarkButton()
}
})
}
}
}
HomeView
struct HomeView: View {
@EnvironmentObject private var vm: HomeViewModel
@State private var showPortfolio: Bool = false
@State private var showPortfolioView: Bool = false
var body: some View {
ZStack {
// Background layer
Color.theme.background
.ignoresSafeArea()
.sheet(isPresented: $showPortfolioView) {
PortfolioView()
.environmentObject(vm)
}
VStack {
homeHeader
HomeStatsView(showPortfolio: $showPortfolio)
SearchBarView(searchText: $vm.searchText)
columnTitles
if !showPortfolio {
allCoinsList
.transition(.move(edge: .leading))
}
if showPortfolio {
portfolioCoinsList
.transition(.move(edge: .trailing))
}
Spacer(minLength: 0)
}
}
}
}
The easiest way to get this up and running would be to move the dismiss
functionality into the view that needs to be dismissed.
The problem with the current approach is you are attempting to dismiss the X button, not the actual view.
struct XMarkButton: View {
var body: some View {
Image(systemName: "xmark") //changed to image, can change color here if needed
.font(.headline)
}
}
struct PortfolioView: View {
@Environment(\.dismiss) private var dismiss // moved dismiss functionality here
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
Text("Portfolio")
}
}
.navigationTitle("Edit Portfolio")
.toolbar(content: {
ToolbarItem(placement: .navigationBarLeading) {
XMarkButton().onTapGesture { // on tap gesture calls dismissal
dismiss()
}
}
})
}
}
}
An alternate approach that keeps your same structure would be to use a completion handler - see below:
struct XMarkButton: View {
var action: () -> Void
var body: some View {
Button(action: {
action()
}, label: {
Image(systemName: "xmark")
.font(.headline)
})
}
}
struct PortfolioView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
Text("Portfolio")
}
}
.navigationTitle("Edit Portfolio")
.toolbar(content: {
ToolbarItem(placement: .navigationBarLeading) {
XMarkButton {
dismiss()
}
}
})
}
}
}