.confirmationDialog inside of .swipeActions does not work, iOS 15 - swiftui

With regards to iOS 15, Xcode 13; I am wondering if this is a bug, not properly implemented, or a planned non-functional feature...
With a list that has a .swipeActions that calls a .confirmationDialog the confirmation dialog does not show.
See example:
import SwiftUI
struct ContentView: View {
#State private var confirmDelete = false
var body: some View {
NavigationView {
List{
ForEach(1..<10) {_ in
Cell()
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
confirmDelete.toggle()
} label: {
Label("Delete", systemImage: "trash")
}
.confirmationDialog("Remove this?", isPresented: $confirmDelete) {
Button(role: .destructive) {
print("Removed!")
} label: {
Text("Yes, Remove this")
}
}
}
}
}
}
}
struct Cell: View {
var body: some View {
Text("Hello")
.padding()
}
}

Misconfiguration:
The view modifier .confirmationDialog needs to be added to the view that is outside of the .swipeActions view modifier. It works when configured properly as shown below:
import SwiftUI
struct ContentView: View {
#State private var confirmDelete = false
var body: some View {
NavigationView {
List{
ForEach(1..<10) {_ in
Cell()
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
confirmDelete.toggle()
} label: {
Label("Delete", systemImage: "trash")
}
}
//move outside the scope of the .swipeActions view modifier:
.confirmationDialog("Remove this?", isPresented: $confirmDelete) {
Button(role: .destructive) {
print("Removed!")
} label: {
Text("Yes, Remove this")
}
}
}
}
}
}
struct Cell: View {
var body: some View {
Text("Hello")
.padding()
}
}

Related

How to avoid the .searchable from appearing and disappearing?

I am having a lot of buggy behavior on .searchable, on iOS 16.1 (Xcode 14.1). As you can see in the screenshot below. When entering a view with a .searchable component it will overlap with the view in transition and then disappears.
I am trying to make the code as basic as possible.
struct ContentView: View {
var body: some View {
NavigationStack {
List {
NavigationLink {
Mailbox().navigationTitle("Inkomend")
} label: { Label("Inkomend", systemImage: "tray") }
NavigationLink {
Mailbox().navigationTitle("Verstuurd")
} label: { Label("Verstuurd", systemImage: "paperplane") }
NavigationLink {
Mailbox().navigationTitle("Prullenmand")
} label: { Label("Prullenmand", systemImage: "trash") }
}
.navigationTitle("Postbussen")
.refreshable {}
}
}
}
struct Mailbox: View {
#State private var searchQuery: String = ""
var body: some View {
List {
NavigationLink {
Text("Detail")
} label: {
VStack(alignment: .leading) {
Text("Apple").font(.headline)
Text("Verify your account.")
Text("Fijn dat je deze belangrijke stap neemt om je account te verifiëren.").lineLimit(2).foregroundColor(.secondary)
}
}
}
.searchable(text: $searchQuery)
}
}
Use NavigationView instead of NavigationStack.
Like Latin Bhuva said, the new NavigationStack is not intended to be used in this way, a NavigationView would be more correct in this case.
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink {
Mailbox().navigationTitle("Inkomend")
} label: { Label("Inkomend", systemImage: "tray") }
NavigationLink {
Mailbox().navigationTitle("Verstuurd")
} label: { Label("Verstuurd", systemImage: "paperplane") }
NavigationLink {
Mailbox().navigationTitle("Prullenmand")
} label: { Label("Prullenmand", systemImage: "trash") }
}
.navigationTitle("Postbussen")
.refreshable {}
}
}
}

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 NavigationLink in the TitleBar pushes the view twice, a Button doing the same thing is not

This is annoying. The Edit button in the NavigationBar pushes the View twice. I made a test button which behaves correctly doing the same thing:
import SwiftUI
struct DetailListPage: View {
#Environment(\.presentationMode) var presentationMode
var listName: ListNames
// #State private var isEditDetailListPageShowing = false
#State private var selection: String? = nil
var body: some View {
Form {
Section(header: Text(listName.title ?? "")
.font(.title)
.padding()) {
Text(listName.listDetail ?? "Nothing is set yet!")
.multilineTextAlignment(.leading)
.padding()
.cornerRadius(12)
}
NavigationLink(destination: EditDetailListPage(listName: listName)) {
Button {
} label: {
Text("Edit Page")
}
}
}
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
self.presentationMode.wrappedValue.dismiss()
} label: {
Text("Cancel")
} .padding()
//Edit List Detail
}
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(
destination: EditDetailListPage(listName: listName)) {
Text("Edit")
}
}
}
}
The Text("Edit") right above is pushing the view twice.
The Button above it acts correctly. Would like to use the navigationbaritem instead of the button.
Works well for me, on macos 11.4, xcode 12.5, target ios 14.5 and macCatalyst 11.3.
Probably some other code (or settings/system) that is causing the issue.
What system are you using? Show us the missing code and how you call the views. Let us know if the test code below does not work for you.
This is the test code I used:
#main
struct TestErrorApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct EditDetailListPage: View {
var listName: [String]
var body: some View {
Text("EditDetailListPage")
}
}
struct ContentView: View {
var body: some View {
NavigationView {
DetailListPage(listName: ["test var"])
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct DetailListPage: View {
#Environment(\.presentationMode) var presentationMode
var listName: [String]
// #State private var isEditDetailListPageShowing = false
#State private var selection: String? = nil
var body: some View {
Form {
Section(header: Text("header string")
.font(.title)
.padding()) {
Text("Nothing is set yet!")
.multilineTextAlignment(.leading)
.padding()
.cornerRadius(12)
}
NavigationLink(destination: EditDetailListPage(listName: listName)) {
Button {
} label: {
Text("Edit Page")
}
}
}
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
self.presentationMode.wrappedValue.dismiss()
} label: {
Text("Cancel")
} .padding()
//Edit List Detail
}
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(
destination: EditDetailListPage(listName: listName)) {
Text("Edit")
}
}
}
}
}
struct ListFrontPage: View {
#Environment(\.presentationMode) var presentationMode
#Environment(\.managedObjectContext) var managedObjectContext
#State private var isAddNewListShowing = false
var listNames: FetchRequest<ListNames>
init() {
listNames = FetchRequest<ListNames>(entity: ListNames.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ListNames.sort, ascending: true)], animation: .default)
}
var body: some View {
VStack {
List() {
Text("Accounts")
.frame(maxWidth: .infinity)
.font(.system(size: 30, weight: .heavy, design: .default))
ForEach (listNames.wrappedValue) { listName in
NavigationLink(destination: DetailListPage(listName: listName)) {
Text("\(listName.title ?? "")")
}
}
.onDelete(perform: deleteItems)
.onMove(perform: moveItem)
}
Spacer()
ZStack {
NavigationLink(destination: AddNewList()) {
Image(systemName: "plus.circle.fill").font(.system(size: 64))
}
}
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button {
self.presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.backward")
Label("Back", image: "")
}, trailing: EditButton())
.navigationBarTitle(Text("Account Management"))
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { listNames.wrappedValue[$0] }.forEach(CoreDataHelper.sharedManager.deleteItems)
}
}
private func moveItem(from offsets: IndexSet, to destination: Int)
{
print("From: \(String(describing: offsets)) To: \(destination)")
// Make an array of items from fetched results
var revisedItems: [ ListNames ] = listNames.wrappedValue.map{ $0 }
// change the order of the items in the array
revisedItems.move(fromOffsets: offsets, toOffset: destination )
// update the userOrder attribute in revisedItems to
// persist the new order. This is done in reverse order
// to minimize changes to the indices.
for reverseIndex in stride( from: revisedItems.count - 1,
through: 0,
by: -1 )
{
revisedItems[ reverseIndex ].sort =
Int16( reverseIndex )
}
}
}

How to navigate to different views in a side bar in swift?

How do I navigate to other views I have created in swiftui?
below Is some code for the side bar I tried doing myself. the issue I was having is a white screen shows up as a huge white button.
VStack {
NavigationView {
HStack {
NavigationLink(destination: SettingsView()) {
Text("Settings")
.font(.system(size:20))
.foregroundColor(.black)
}
}
NavigationLink(destination: Settings()) {
Text("Settings")
.font(.title2)
}
}
NavigationLink(destination: AboutUs()) {
Text("About us")
.font(.title2)
}
}
}
}
Unless you're within a NavigationView, which has very specific appearances on iOS and macOS, you wouldn't be using NavigationLink. Since you're making your own sidebar, that means you won't be using Navigation View/Link.
Instead, you can use a #State variable or ObservableObject with a #Published property that keeps track of what view is active. I chose the latter in this example:
enum ViewTypes {
case main
case settings
case aboutUs
}
class SidebarNavigationManager : ObservableObject {
#Published var viewType : ViewTypes = .main
}
struct ContentView: View {
#StateObject var navigationManager = SidebarNavigationManager()
var body: some View {
HStack(alignment: .top) {
SidebarView(navigationManager: navigationManager)
.frame(width: 100)
.frame(maxHeight: .infinity)
.border(Color.green)
//Main content
VStack {
switch navigationManager.viewType {
case .main:
MainView()
case .settings:
SettingsView()
case .aboutUs:
AboutUsView()
}
}.frame(maxWidth: .infinity)
}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct SidebarView : View {
#ObservedObject var navigationManager : SidebarNavigationManager
var body: some View {
//Sidebar
VStack {
Button(action: { navigationManager.viewType = .main }) {
Text("Main")
}
Button(action: { navigationManager.viewType = .settings }) {
Text("Settings")
}
Button(action: { navigationManager.viewType = .aboutUs }) {
Text("About Us")
}
}
}
}
struct MainView : View {
var body: some View {
Text("Main")
}
}
struct SettingsView : View {
var body: some View {
Text("Settings")
}
}
struct AboutUsView : View {
var body: some View {
Text("About Us")
}
}

Any ideas how to help this Form + ForEach SwiftUI view update it’s NavigationLink Destination?

I’m seeing this issue where a Form / NavigationLink set up seems to stop passing through the data on changes.
Expected behavior: Checkmarks should update when you pick a different food.
Observed behavior: You can see the favorite food changing outside the NavigationLink Destination, but not inside.
This setup mirrors a dynamic application where a ForEach is used to display various NavigationLinks in the Form based on parent data. Weirdly enough, this works if you replace Form with VStack, so I’m curious why this isn’t updating.
I have attached two minimum-setup example projects that replicate this issue where the destination of a NavigationLink is not receiving an update when data is changing. One with Binding, one with simpler passed properties.
Sample Project #1 with Binding - Dropbox
Sample Project #2 without Binding - Dropbox
Code #1:
//
// ContentView.swift
// Form Updating Example
//
// Created by Sahand Nayebaziz on 12/10/20.
//
import SwiftUI
struct ContentView: View {
#State var isPresentingMainView = false
#State var favoriteFood: FoodType = .bagel
var body: some View {
VStack {
Button(action: { isPresentingMainView = true }, label: {
Text("Present Main View")
})
}
.fullScreenCover(isPresented: $isPresentingMainView) {
MainView(favoriteFood: $favoriteFood)
}
}
}
struct MainView: View {
#Binding var favoriteFood: FoodType
var body: some View {
NavigationView {
HStack {
Spacer()
Text(favoriteFood.emoji)
.font(.title)
.foregroundColor(.secondary)
Spacer()
NavigationView {
Form {
List {
ForEach(["SomethingRepresentingShowingFood"], id: \.self) { _ in
NavigationLink(
destination: makeDetail(),
label: {
Text("Food Randomizer")
})
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: 350)
}
.navigationTitle("Main")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(StackNavigationViewStyle())
}
func makeDetail() -> some View {
Form {
ForEach(FoodType.allCases) { foodType in
Button(action: { favoriteFood = foodType }, label: {
HStack {
Text(foodType.emoji)
Spacer()
if favoriteFood == foodType {
Image(systemName: "checkmark")
}
}
})
}
}
}
}
enum FoodType: String, Identifiable, CaseIterable {
case bagel, pizza, broccoli
var id: String { rawValue }
var emoji: String {
switch self {
case .bagel: return "🥯"
case .pizza: return "🍕"
case .broccoli: return "🥦"
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
MainView(favoriteFood: .constant(.bagel))
MainView(favoriteFood: .constant(.bagel))
.makeDetail()
}
}
}
Code #2:
//
// ContentView.swift
// Form Updating Example
//
// Created by Sahand Nayebaziz on 12/10/20.
//
import SwiftUI
struct ContentView: View {
#State var isPresentingMainView = false
#State var favoriteFood: FoodType = .bagel
var body: some View {
VStack {
Button(action: { isPresentingMainView = true }, label: {
Text("Present Main View")
})
}
.fullScreenCover(isPresented: $isPresentingMainView) {
MainView(currentFavoriteFood: favoriteFood, onUpdateFavoriteFood: { favoriteFood = $0 })
}
}
}
struct MainView: View {
let currentFavoriteFood: FoodType
let onUpdateFavoriteFood: (FoodType) -> Void
var body: some View {
NavigationView {
HStack {
Spacer()
Text(currentFavoriteFood.emoji)
.font(.title)
.foregroundColor(.secondary)
Spacer()
NavigationView {
Form {
List {
ForEach(["SomethingRepresentingShowingFood"], id: \.self) { _ in
NavigationLink(
destination: makeDetail(),
label: {
Text("Food Randomizer")
})
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: 350)
}
.navigationTitle("Main")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(StackNavigationViewStyle())
}
func makeDetail() -> some View {
Form {
ForEach(FoodType.allCases) { foodType in
Button(action: { onUpdateFavoriteFood(foodType) }, label: {
HStack {
Text(foodType.emoji)
Spacer()
if currentFavoriteFood == foodType {
Image(systemName: "checkmark")
}
}
})
}
}
}
}
enum FoodType: String, Identifiable, CaseIterable {
case bagel, pizza, broccoli
var id: String { rawValue }
var emoji: String {
switch self {
case .bagel: return "🥯"
case .pizza: return "🍕"
case .broccoli: return "🥦"
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
MainView(currentFavoriteFood: .bagel, onUpdateFavoriteFood: { _ in })
MainView(currentFavoriteFood: .bagel, onUpdateFavoriteFood: { _ in })
.makeDetail()
}
}
}