I have a custom alert which I am presenting from sheet or from regular view as well. I also have a scrim behind the alert covering entire sheet (which am not sure why it’s not showing up in this sample code) and the sheet also has visible drag indicator (which again is not showing up in sample code).
Anyways, my questions are:
- How do I disable navigation back button when custom alert is presented?
- Scrim covers the sheet, but the slightly visible view on top behind the sheet isn’t covered. Is there a way to cover entire height of the screen with scrim?
- Is there a way to disable drag indicator of the sheet when custom alert is present?
iOS version I am using is 16.4
Code:
import SwiftUI
import Foundation
struct ContentView: View {
@State private var showDetails = false
var body: some View {
NavigationStack {
VStack {
Button("Show Sheet A") {
showDetails = true
}
}
.sheet(isPresented: $showDetails) {
ContentView1()
}
}
}
}
struct ContentView1: View {
@State private var showDetails = false
var body: some View {
NavigationStack {
VStack {
Button("Show Sheet B") {
showDetails = true
}
}
.navigationDestination(isPresented: $showDetails) {
ContentView2()
}
.presentationDragIndicator(.visible)
}
}
}
struct ContentView2: View {
@State private var showAlert = false
var body: some View {
VStack {
Button("Show Alert") {
showAlert = true
}
}
.navigationTitle("settings")
.customAlert(isPresented: $showAlert)
.presentationDragIndicator(.visible)
}
}
struct CustomAlertView: ViewModifier {
@Binding var isPresented: Bool
init(isPresented: Binding<Bool>) {
self._isPresented = isPresented
}
func body(content: Content) -> some View {
content.animation(nil, value: self.$isPresented.wrappedValue)
.overlay(self.$isPresented.wrappedValue ? Color.gray.opacity(0.4) : nil) /// Background scrim which not sure why it's not visible.
.overlay(self.$isPresented.wrappedValue ? alertContent() : nil)
.animation(.default, value: self.$isPresented.wrappedValue)
}
@ViewBuilder
private func alertContent() -> some View {
GeometryReader { geometry in
if self.$isPresented.wrappedValue {
VStack {
Text("title").foregroundColor(Color.black).font(.title2).bold().lineLimit(nil)
.padding([.leading, .trailing], 20.0).padding(.top, 16.0)
Spacer()
Text("message").foregroundColor(Color.secondary).font(.body).bold().lineLimit(nil)
.padding([.leading, .trailing], 20.0).padding(.top, 16.0)
Spacer()
Button { } label: {
Text("Click Me")
}
.frame(width: 250.0, height: 20.0).padding(10.0).background(Color.blue)
.foregroundColor(.white).font(.body)
}.fixedSize(horizontal: false, vertical: true).background(Color.gray).cornerRadius(28)
.clipped().padding([.leading, .trailing], 5.0)
.position(x: geometry.size.width/2, y: geometry.size.height/2).frame(width: 328.0)
}
}
}
}
extension View {
func customAlert(isPresented: Binding<Bool>) -> some View {
return modifier(CustomAlertView(isPresented: isPresented))
}
}
I tried setting .presentationBackgroundInteraction(.disabled)
on content within custom alert modifier, but that didn’t work either.
2
Answers
You can use your showAlert variable to do that, just add
.disabled(showAlert)
in your NavigationStack. For extra effect, you can also add.opacity(showAlert ? 0.2 : 1)
It’s a rather simple change. Just check if the alert is presented, and use the appropriate modifiers to hide the back button + disable the swipe to dismiss gesture.
To use full screen presentation, you have two options: