[SwiftUI] Presenting an Alert or a View from a View
Any View
in SwiftUI framework could present an Alert
or another View
in modal presentation style.
Absolutely, to present them we need create Them
first. :)
An Alert
.
Alert(title: Text("Alert")
, message: Text("Information is data, data is king")
, dismissButton: .default(Text("Ok")))
and a view to be presented
struct PresentedView : View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
VStack {
Button("Dismiss") {
self.presentationMode.wrappedValue.dismiss()
}.padding()
}
}
}
Then we try to present them in our main view
struct ContentView: View {
@State var showAlert: Bool = false
@State var presentingView: Bool = false
var body: some View {
VStack {
Button("Present Alert") {
self.showAlert.toggle()
}.alert(isPresented: $showAlert) { () -> Alert in
Alert(title: Text("Alert")
, message: Text("Information is data, data is king")
, dismissButton: .default(Text("Ok")))
}.padding()
Button("Present View") {
self.presentingView.toggle()
}.sheet(isPresented: $presentingView) { () -> PresentedView in
PresentedView()
}
}
}
}
The code is easy to understand. When button was tapped, the @State
boolean variable will be flipped. So alert will be shown or view will be presented.
There's a big concern here. How could we make the content of alert dynamic, and pass additional data to presented view? Hmm...
Let's check document a bit https://developer.apple.com/documentation/swiftui/view. I found something:
Document says that, we could not only control Alert/Sheet
by Bool
binding, but also Optional
binding. Yay! As I guess, the presented view will be presented when optional binding has value. Try it now...
We need to create two class that implement Identifiable
protocol. Those class is our Optional Binding
data.
class AlertContent : Identifiable {
var message: String
init(_ msg: String) {
self.message = msg
}
}
class PresentContent : ObservableObject, Identifiable {
typealias PresentData = String
var message: PresentData
init(_ msg: String) {
self.message = msg
}
}
Then we modify of our views a little
struct ContentView: View {
@State var showAlert: AlertContent? = nil //content of alert
@State var presentingData: PresentContent? = nil //content of present
var body: some View {
VStack {
Button("Present Alert") {
self.showAlert = AlertContent("\(Date().description)")
}.alert(item: $showAlert) { item -> Alert in
Alert(title: Text("Alert")
, message: Text("\(item.message)")
, dismissButton: .default(Text("Ok")))
}.padding()
Button("Present View") {
self.presentingData = PresentContent("Data is passed from content View")
}.sheet(item: $presentingData) { item -> PresentedView in
PresentedView(item) // should define init method for our presented view
}
}
}
}
struct PresentedView : View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@ObservedObject var content: PresentContent
init(_ data: PresentContent) {
self.content = data
}
var body: some View {
VStack {
Text("\(content.message)")
.padding()
Button("Dismiss") {
self.presentationMode.wrappedValue.dismiss()
}.padding()
}
}
}
Okay, that's some basic about presenting and alerting users with SwiftUI. Our @State
variables could be grouped in one @ObservedObject var viewModel: ContentViewModel
class for make our code cleaner. Then we could do logic-only with Combine
framework easily.
That's all for today.
Happy Coding.