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

126 Views Asked by At

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().

1

There are 1 best solutions below

5
MatBuompy On BEST ANSWER

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!