Rojer
Rojer

Reputation: 13

DismissAction for Preview

I am trying to solve an issue with DismissAction in Preview. This is in the View: let parentDismiss: DismissAction

This is what I have in Preview:

struct MilkingView_Previews: PreviewProvider {
    static var previews: some View {
        MilkingView(parentDismiss: DismissAction())
    }
}

But I constantly have an error 'DismissAction' cannot be constructed because it has no accessible initializers

DismissAction() doesn't help as well as DismissAction.

Upvotes: 1

Views: 674

Answers (6)

Mihai Fischer
Mihai Fischer

Reputation: 339

on iOS 17 you have @Previewable that lets you add an environment variable to the preview section. On older versions of iOS, you will get a warning for this.

#Preview {
   @Previewable @Environment(\.dismiss) var dismiss
   MilkingView(parentDismiss: dismiss)
 }

Upvotes: 0

George Ef
George Ef

Reputation: 1

Declare let parentDismiss: DismissAction?.

In the app parentDismiss is non-nil, in Preview set it to nil.

Upvotes: 0

RyloRiz
RyloRiz

Reputation: 87

Incredibly, I've found all you need is to ignore the property altogether. The following code compiles & clicking the dismiss button performs no action at all.

import SwiftUI

struct MySheet: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        NavigationStack {
            VStack {
                Text("It worked!")
            }
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button("Cancel") {
                        dismiss()
                    }
                }
            }
        }
    }
}

#Preview {
    MySheet()
}

Upvotes: 0

Baker2795
Baker2795

Reputation: 247

For other's who come across this. Define your dismiss action in the struct as a static property.

@Environment(\.dismiss) static private var dismiss

Upvotes: 1

Apollo
Apollo

Reputation: 1986

I don't know how to solve your problem the right way, but I am using a workaround, and it works for me.

So, bear with me.

I have a .sheet that has a view inside. That view has a list of NavigationLink items, and they will open a form in a third view. From that form I should be able to close the sheet.

So..

Main view > List (in a sheet) > Form (close from here)

So on my list view I have this:

@Environment(\.dismiss) var dismiss
// (...)
NavigationLink {
    MyForm(something, dismissParent: dismiss)
} label: {
    Text(title)
}

On my form view, that lives inside that list, I pass my dismiss into the initialiser. The init originally looked like this:

private var dismissParent: DismissAction

init(_ something, dismissParent: DismissAction) {
    self.something = something
    self.dismissParent = dismissParent
}

Of course, that breaks the preview, as your own code. What I did was pass a closure as a callback. So I modified my code, like so:

private var dismissParent: () -> Void

init(_ something, dismissParent: @escaping () -> Void) {
    self.something = something
    self.dismissParent = dismissParent
}

On the parent I got this:

@Environment(\.dismiss) var dismiss
// (...)
NavigationLink {
    MyForm(something) {
        dismiss()
    }
} label: {
    Text(title)
}

And on the preview I do:

#if DEBUG
struct MyForm_Previews: PreviewProvider {
    static var previews: some View {
        ZStack {
            MyForm(
                dummySomething,
                dismissParent: {}
            )
        }
    }
}
#endif

I use that ZStack there because preview for some reason seems to break a lot when I'm dealing with views in sheets. I don't know why, but this fixes it.

I also use annotation #if DEBUG and #endif because I realised that the preview code is compiled in some situations and it bloats the app needlessly. I use that to exclude preview from the final code. (I don't know if that was fixed or not, I don't have time or patience to pursue this kind of information, but it doesn't break my code, so I'm happy.)

It works for me. Try that.

Upvotes: 0

Yrb
Yrb

Reputation: 9675

This should be enough to make the preview build, if you simply need to provide a function, but don't need it to actually do anything:

struct MilkingView_Previews: PreviewProvider {
    static var previews: some View {
        MilkingView(parentDismiss: {})
    }
}

if you do, you can always do something like:

struct MilkingView_Previews: PreviewProvider {
    static var previews: some View {
        MilkingView(parentDismiss: {
            print("Doing something...")
        })
    }
}

There is no need to do more than this. It is a preview provider showing just part of the UI.

Upvotes: 1

Related Questions