Button with Label and Alert - swiftui

I have this button
Button { }
label: {
Image(systemName: "info.circle")
}
and want to couple it with this alert
.alert(isPresented: $showAlert) {
Alert(title: Text("Impressum"), message: Text("?"), dismissButton: .default(Text("Okay")))
Using SwiftUI... It's not working.

You need to actually toggle the showAlert state.
Code:
struct ContentView: View {
#State private var showAlert = false
var body: some View {
Button {
showAlert.toggle()
} label: {
Image(systemName: "info.circle")
}
.alert(isPresented: $showAlert) {
Alert(title: Text("Impressum"), message: Text("?"), dismissButton: .default(Text("Okay")))
}
}
}
Result:

Related

SwfitUI - Cannot open sheet from a button that is a picker option

I want to open navigate to a view where I can add another picker option.
I tried the followings:
Picker(NSLocalizedString("Pick an option", comment: ""), selection: $value, content: {
ForEach(options, id: \.self){ option in
Text(option).tag(option)
}
Button(action: {
sheetIsPresented.toggle()
}, label: {
Image(systemName: "plus")
})
.sheet(isPresented: $sheetIsPresented){
AddView()
}
})
A button does show at the end of the options.
However, when the button is clicked, nothing happened. AddView() doesn't show up...
Is there a way to make this work?
try this approach, as shown in this example code:
struct ContentView: View {
#State var sheetIsPresented = false
#State var options = ["item-1","item-2","item-3"]
#State var value = "item-1"
var body: some View {
VStack (spacing: 88){
Picker(NSLocalizedString("Pick an option", comment: ""), selection: $value) {
ForEach(options, id: \.self){ option in
Text(option).tag(option)
}
}
Button(action: { sheetIsPresented.toggle() }) {
Image(systemName: "plus")
}
.sheet(isPresented: $sheetIsPresented) {
AddView(options: $options)
}
}
}
}
struct AddView: View {
#Binding var options: [String]
var body: some View {
// add some random option
Button(action: { options.append(UUID().uuidString) }) {
Image(systemName: "plus")
}
}
}
You can open a sheet with a menu if you put the .sheet on the menu element
example:
struct TestView: View {
#State var sheetIsPresented: Bool = false
var body: some View {
Menu("Actions") {
Button(action: {
sheetIsPresented.toggle()
}, label: {
Image(systemName: "plus")
})
}
.sheet(isPresented: $sheetIsPresented){
VStack{
Text("Hello")
}
}
}
}

SwiftUI: two column NavigationView not loading details after rotation?

I have the following SwiftUI code:
struct ContentView: View {
var body: some View {
NavigationView {
Form {
Section {
NavigationLink {
DetailsView()
} label: {
Text("Show details")
}
}
}
Text("Select details")
}
}
}
struct DetailsView: View {
#State var showModal = false
var body: some View {
ZStack {
Color.gray.ignoresSafeArea()
VStack {
Spacer()
Button {
showModal.toggle()
} label: {
Text("Show modal")
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
}
}
}
.fullScreenCover(isPresented: $showModal) {
MyModalView {
showModal = false
}
}
}
}
struct MyModalView: View {
var someAction:()->()
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
Button {
someAction()
} label: {
Text("some action")
.foregroundColor(.white)
}
}
}
}
I am experiencing the following bug, where tapping on "Show details" won't show the DetailsView anymore after rotation...
How can I fix this?
Navigation view is really problematical but will be improved with IOS 16. Here is my solution
struct ContentView: View {
#State var navigate = false
var body: some View {
NavigationView {
Form {
Section {
NavigationLink(destination: DetailsView(navigate: $navigate), isActive: $navigate){
Text("Show details")
}
}
}
Text("Select details")
}
.navigationViewStyle(.automatic)
}
}
struct DetailsView: View {
#Binding var navigate: Bool
#State var showModal = false
var body: some View {
ZStack {
Color.gray.ignoresSafeArea()
VStack {
Spacer()
Button {
showModal.toggle()
} label: {
Text("Show modal")
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
}
}
}
.fullScreenCover(isPresented: $showModal) {
MyModalView {
showModal = false
}
}
.onAppear(){
navigate = false
}
}}
struct MyModalView: View {
var someAction:()->()
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
Button {
someAction()
} label: {
Text("some action")
.foregroundColor(.white)
}
}
}}

SwiftUI: Why is second alert not showing?

In the following simple example, why is Alert 2 not showing up?
struct ContentView: View {
#State private var showingOtherButton = false
#State private var showingAlert1 = false
#State private var showingAlert2 = false
var body: some View {
Group {
if showingOtherButton {
Button("Show Alert 2") {
showingAlert2 = true
}
.alert(isPresented: $showingAlert2) {
print("Alert 2 should be shown")
return Alert(title: Text("Alert 2"), dismissButton: .default(Text("Ok")))
}
} else {
Button("Show other Button") {
self.showingOtherButton = true
}
Button("Show Alert 1") {
showingAlert1 = true
}
}
}
.alert(isPresented: $showingAlert1) {
Alert(title: Text("Alert 1"), dismissButton: .default(Text("OK")))
}
}
}
Placing the alert 1 modifier onto the "Show Alert 1" button directly makes it work, but I don't understand why.
Your second alert should go in the same place as the first - attached to the Group view, not to the Button:
Group {
if showingOtherButton {
Button("Show Alert 2") {
showingAlert2 = true
}
} else {
Button("Show other Button") {
self.showingOtherButton = true
}
Button("Show Alert 1") {
showingAlert1 = true
}
}
}
.alert(isPresented: $showingAlert1) {
Alert(title: Text("Alert 1"), dismissButton: .default(Text("OK")))
}
.alert(isPresented: $showingAlert2) {
print("Alert 2 should be shown")
return Alert(title: Text("Alert 2"), dismissButton: .default(Text("Ok")))
}

SwiftUI: After I dismiss a sheet all buttons inside navigationBarItems do not work anymore

after I dismiss a sheet my buttons on the screen above do not work anymore. Only after pressing on a non-interacting surface the buttons work again. I use swift version 5 and the error occurs in the simulator and on the device.
#Edit
Code Snippets
AddView this will be displayed in a sheet
struct AddView: View {
#Environment(\.managedObjectContext) var moc
#Environment(\.presentationMode) var presentationMode
// some state
var body: some View {
NavigationView {
Form {
Section(header: Text("Name")) {
TextField("Task-Name (e.g. Eat the 🍰)", text: $title)
}
Section(header: Text("Settings")) {
DatePicker("Date", selection: $timestamp, displayedComponents: .date)
Toggle(isOn: $highPrio) {
Text("High Priority")
}
}
}
.navigationBarItems(trailing: Button("Add"){
// logic
do {
try self.moc.save()
} catch {
print(error.localizedDescription)
}
self.presentationMode.wrappedValue.dismiss()
}.alert(isPresented: $showAlert) {
Alert(title: Text("Name field is empty"), message: Text("Please enter a name"), dismissButton: .default(Text("Got it!")))
})
.navigationBarTitle("New Task")
}
}
}
struct AddView_Previews: PreviewProvider {
static var previews: some View {
AddView()
}
}
ContentView includes a FetchRequest with some functions and nothing more.
struct ContentView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: Task.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \Task.timestamp, ascending: true),
NSSortDescriptor(keyPath: \Task.status, ascending: false),
NSSortDescriptor(keyPath: \Task.highPriority, ascending: false),
]) var tasks: FetchedResults<Task>
#State private var showingAddSheet = false
#State private var showAlert = false
#State private var editMode = false
var body: some View {
NavigationView {
List {
ForEach(tasks.filter{return self.filterTasks(task: $0)}, id: \.self) { task in
HStack {
TaskRowView(
title: task.wrappedTitle,
status: task.wrappedStatus,
timestamp: task.wrappedTimestamp,
highPriority: task.highPriority,
showDetail: self.editMode
).onTapGesture {
self.toggleStatus(item: task)
}
}
}
.onDelete(perform: removeTask)
}
.navigationBarTitle(self.editMode ? "All Tasks" : "Today")
.navigationBarItems(leading: Button(self.editMode ? "Done" : "Edit") {self.editMode.toggle()}, trailing: Button("Add") {self.showingAddSheet.toggle()})
.sheet(isPresented: $showingAddSheet) {
AddView().environment(\.managedObjectContext, self.moc)
}
}.onAppear(perform: {
self.cleanupTasks()
}).alert(isPresented: $showAlert) {
Alert(title: Text("Unfished Task found"),
message: Text("Do you want to take over the old tasks or delete them?"),
primaryButton: .destructive(Text("Delete all")) {
self.removeOldTasks()
},
secondaryButton: .default(Text("Take over")) {
self.takeOldTasksOver()
}
)
}
}
// functions...
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return ContentView().environment(\.managedObjectContext, context)
}
}
#endif
Solution
This is a Bug that is related to the .large navigationBarItem. You can set that to .inline to go around it for now:
NavigationView {
,,,
.navigationBarTitle(Text(""), displayMode: .inline)
}
Related Thread: SwiftUI - Navigation bar button not clickable after sheet has been presented
The problem happens when there is a navigationView inside your "AddView" struct. From what I have tested, If you remove the navigationView and just use a button (for dismissal) somewhere else inside the AddView it works perfectly. as below:
var body: some View {
VStack{
HStack {
Spacer()
Button(action: {
// logic ..
self.presentationMode.wrappedValue.dismiss()
}){
Text("Add")
}.alert(isPresented: $showAlert) {
Alert(title: Text("Name field is empty"), message: Text("Please enter a name"), dismissButton: .default(Text("Got it!")))
}
.padding(24)
}
Form {
Section(header: Text("Name")) {
TextField("Task-Name (e.g. Eat the 🍰)", text: $title)
}
Section(header: Text("Settings")) {
DatePicker("Date", selection: $timestamp, displayedComponents: .date)
Toggle(isOn: $highPrio) {
Text("High Priority")
}
}
}
}
}
I have this problem in the simulator, but on a real device it works well. Consider updating of xcode, mac OS or iOS.
It's working on device with latest Xcode.

ForEach(0...5,id: \.self) { n in --what is the scope of "n"?

See the following TestView. When I clicked "Button 5", I see the alert shows "Button 0 clicked".
struct TestView: View {
#State var showAlert = false
var body: some View {
VStack {
ForEach(0...5, id: \.self) { n in
Button(action: {
self.showAlert.toggle()
}) {
Text("Button \(n)")
}
.padding(20)
.border(Color.green, width: 4)
.padding(8)
.alert(isPresented: self.$showAlert) {
Alert(title: Text("Button \(n) clicked"))
}
}
}
}
}
Regardless of which button I click, the alert always show "Button 0 clicked". I was expecting each button should show its own button index. Wondering why is that?
I suspect this is happening because everything in your TestView re-renders while state variable changes. So your alert is displaying only for the first loop iteration. The decision is to change other variable, which should contain clicked button index:
struct TextView: View {
#State var showAlert = false
#State var clickedButtonIndex = 0
var body: some View {
VStack {
ForEach(0...5, id: \.self) { n in
Button(action: {
self.showAlert.toggle()
self.clickedButtonIndex = n
}) {
Text("Button \(n)")
}
.padding(20)
.border(Color.green, width: 4)
.padding(8)
.alert(isPresented: self.$showAlert) {
Alert(title: Text("Button \(self.clickedButtonIndex) clicked"))
}
}
}
}
}
And you'll have this result: