iosswiftswiftuicontextmenubeta

Show a different view in context menus SwiftUI


I'm developing a Mastodon client in SwiftUI and I'd like to allow users to see a preview of a post from the context menu, as many other apps do.

I know I can achieve this in UIKit but I haven't seen how in SwiftUI.

Let's say, for example, that I've got two views: CompactView and ExpandedView. CompactView is composed of an Image and a Text with a line limit of 2 so that every other line is hidden. ExpandedView is also composed of an Image and a Text, but the text's line limit is 20 so that all the post content is shown.

If the user force-touches on the CompactView, I'd like to show ExpandedView in the context menu instead of CompactView. How can I achieve this?

struct ContentView: some View {

    var body: some View {
        List(0 ..< 5) {
            CompactView()
        }
    }
}

struct CompactView: some View {

    var body: some View {
        HStack {
            Image("profile")
            Text(/*Post content goes here*/)
                .lineLimit(2)
        }
            .contextMenu {
                Button("Report", action: {})
            }
    }
}

struct ExpandedView: some View {

    var body: some View {
        HStack {
            Image("profile")
            Text(/*Post content goes here*/)
                .lineLimit(20)
        }
    }
}

My codebase isn't like this, but the approach would be the same I guess.

TL;DR: How can I show a different view when the user force-touches a view in SwiftUI?

Thanks in advance.


Solution

  • First we need this - Simple way to implement preview context menu for SwiftUI

    My content view for example

    struct ContentView: View {
    
    let deleteAction = UIAction(
    title: "Remove rating",
    image: UIImage(systemName: "delete.left"),
    identifier: nil,
        attributes: UIMenuElement.Attributes.destructive, handler: {_ in print("Foo")})
    
    var body: some View {
        NavigationView {
            Text("Hello, World!")
                .contextMenu(PreviewContextMenu(destination: Text("Destination"), actionProvider: { items in
                    return UIMenu(title: "My Menu", children: [deleteAction])
                }))
        }
    }}
    

    It's gonna look like that