swiftswiftuiswiftui-ontapgesture

How can I expand my .contextMenu preview to fullscreen on tap of preview?


I am trying to make my SwiftUI .contextMenu preview, in a forEach view, to be tappable and expandable. Like for example the Apple Stocks app, when I hold a news link, the preview shows up and I am able to tap it to make it expand to fullscreen. I would like to duplicate this behavior, but I don't get how I can do this.

I have tried searching for this but I can't find anything on this. Here is my code for the contextMenu:

if !vm.StockNews.isEmpty {
    ForEach(vm.StockNews, id: \.self) { news in
        if let url = URL(string: news.link) {
            VStack(alignment: .leading) {
                Text("\(news.title)")
                    .font(.title)
                    .fontWeight(.semibold)
                    .padding(EdgeInsets(top: 0, leading: 5, bottom: 5, trailing: 5))
                    .lineLimit(4)
                
                Text("\(news.description)")
                    .font(.headline)
                    .padding(5)
                    .lineLimit(3)
                    .fixedSize(horizontal: false, vertical: true)
                
                Text("\(vm.formattedTimeAgo(from: news.pubDate))")
                    .padding(EdgeInsets(top: 5, leading: 5, bottom: 0, trailing: 5))
            }
            .fixedSize(horizontal: false, vertical: true)
            
            .onTapGesture {
                isPresentingWebView = true
            }
            
            .fullScreenCover(isPresented: $isPresentingWebView) {
                SafariWebView(url: url)
                    .ignoresSafeArea()
            }
            
            
            .contextMenu {
                Button(action: {
                    UIPasteboard.general.string = news.link
                }) {
                    Text("Kopier lenke")
                    Image(systemName: "link")
                }
                
            } preview: {
                SafariWebView(url: url)
                
            }
            
            
        } else {
            VStack(alignment: .leading) {
                Text("\(news.title)")
                    .font(.title)
                    .fontWeight(.semibold)
                    .padding(5)
                    .lineLimit(3)
                
                Text("\(news.description)")
                    .font(.headline)
                    .padding(5)
                    .lineLimit(3)
                    .fixedSize(horizontal: false, vertical: true)
                
                Text("\(news.pubDate)")
                    .padding(5)
            }
            .fixedSize(horizontal: false, vertical: true)
        }
        
    }
} else {
    HStack {
        Spacer()
        Text("Ingen nylige nyheter")
            .font(.title2.bold())
        
        Spacer()
    }
    .padding(.top, -15)
    .padding(.bottom, 30)
    .listRowSeparator(.hidden)
}

And here is the code for the SafariWebView:

import SwiftUI
import SafariServices

struct SafariWebView: UIViewControllerRepresentable {
    let url: URL
    
    func makeUIViewController(context: Context) -> SFSafariViewController {
        let config = SFSafariViewController.Configuration()
        return SFSafariViewController(url: url, configuration: config)
    }
    
    func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
        
    }
}

How could I be able to make my contextMenu preview be tappable and expandable to fill the screen like in the .fullScreenCover().


Solution

  • The best I could come up with was an overlay with some buttons on top of it. I hope you like the result. I used a geometry reader to get the size of the view and use it to expand the WebView to full screen. By the way, the code to modify the Done Button behavior did not work. That's why I was forced to use the xmark to close the web view when using the overlay at least. The fullScreenCover can still be used if you prefer though, I tried and it works.

    struct ContentView: View {
        
        @State private var isPresentingWebView = false
        @State private var showCustomPopover = false
        
        var body: some View {
            GeometryReader { proxy in
                let size = proxy.size
                VStack(alignment: .leading) {
                    Text("Title")
                        .font(.title)
                        .fontWeight(.semibold)
                        .padding(EdgeInsets(top: 0, leading: 5, bottom: 5, trailing: 5))
                        .lineLimit(4)
                    
                    Text("News")
                        .font(.headline)
                        .padding(5)
                        .lineLimit(3)
                        .fixedSize(horizontal: false, vertical: true)
                    
                    Text("Date")
                        .padding(EdgeInsets(top: 5, leading: 5, bottom: 0, trailing: 5))
                }
                .fixedSize(horizontal: false, vertical: true)
                .onLongPressGesture {
                    showCustomPopover = true
                }
                .overlay {
                    if showCustomPopover {
                        VStack {
                            SafariWebView(url: URL(string: "https://stackoverflow.com/questions/77974957/how-can-i-expand-my-contextmenu-preview-to-fullscreen-on-tap-of-preview?noredirect=1#comment137490790_77974957")!)
                                .frame(width: isPresentingWebView ? size.width : 250, height: isPresentingWebView ? size.height : 300)
                                .overlay(alignment: .topLeading) {
                                    HStack {
                                        if isPresentingWebView {
                                            Button("", systemImage: "xmark") {
                                                withAnimation {
                                                    showCustomPopover = false
                                                    isPresentingWebView = false
                                                }
                                            }
                                        }
                                        Button("", systemImage: "arrow.up.left.and.arrow.down.right") {
                                            withAnimation {
                                                isPresentingWebView.toggle()
                                            }
                                        }
                                    }
                                    .offset(x: isPresentingWebView ? 15: -30, y: -30)
                                }
                            
                            if !isPresentingWebView {
                                Button(action: {
                                    UIPasteboard.general.string = "Link here"
                                }) {
                                    Text("Kopier lenke")
                                    Image(systemName: "link")
                                }
                                .buttonStyle(.bordered)
                            }
                        }
                    }
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background {
                    if showCustomPopover {
                        Color.gray.opacity(0.1)
                            .frame(maxWidth: .infinity, maxHeight: .infinity)
                            .onTapGesture {
                                withAnimation {
                                    showCustomPopover = false
                                }
                            }
                    }
                }
                /*.fullScreenCover(isPresented: $isPresentingWebView) {
                    SafariWebView(url: URL(string: "https://stackoverflow.com/questions/77974957/how-can-i-expand-my-contextmenu-preview-to-fullscreen-on-tap-of-preview?noredirect=1#comment137490790_77974957")!)
                        .ignoresSafeArea()
                }*/
            }
        }
            
    }
    

    As I said, if you prefer you can still use the fullScreenCover, just by uncommenting it it shows up (you may want to remove the resizing behavior of the overlay too though).

    Here's the result:

    WebView overlay

    Let me know what you think!