ios – Presenting customized view on high of any view in SwiftUI

ios – Presenting customized view on high of any view in SwiftUI


I do know there are a number of posts on the market associated to related matters, however none labored in my case.

Mainly, I’ve a fancy utility the place there’s a fundamental view and there are a number of panels (offered in type of .sheet or .fullScreenCover over the principle view). Just one panel will probably be energetic at a time over the principle view (just like Apple Maps).

I’ve a customized alert (as a full display screen cowl) created in type of modifier, however each time I need to current an alert over a sheet, I’ve to connect the customized alert modifier to that sheet. That is including quite a lot of duplicate code throughout the app since I’m attaching modifiers to each sheet and I’m attempting out one thing the place the modifier is simply tied to the principle view and each time alert is to be offered, irrespective of which panel/sheet is open on fundamental view, an alert needs to be displayed over the energetic sheet.

Beneath is my code. I’ve tried to create a small pattern working code since my app is advanced:

import SwiftUI

struct ContentView: View {
    @State non-public var showSheet = false
    @StateObject var viewModel: AppViewModel = AppViewModel()
    
    var physique: some View {
        VStack {
            Textual content("That is fundamental view with a number of sheets.")
        }
        .padding(.backside, 350.0)
        .onAppear {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            SheetA().environmentObject(viewModel)
        }
        .customAlert(isPresented: $viewModel.showAlert) /// Want to current an alert all the time from right here, however the alert needs to be seen on high of any sheet that is open.
    }
}

struct SheetA: View {
    @EnvironmentObject var viewModel: AppViewModel
    
    var physique: some View {
        VStack {
            Textual content("That is sheet.")
            Spacer()
            Button {
                viewModel.showAlertFromSheet()
            } label: {
                Textual content("Faucet me")
            }
        }
        .padding([.top, .bottom], 150.0)
        .presentationDetents([.medium, .large], choice: $viewModel.panelDetent)
        // Notice: Works high-quality if I add this modifier to each sheet. However attempting to keep away from that.
        //.customAlert(isPresented: $viewModel.showAlert)
    }
}

class AppViewModel: ObservableObject {
    @Printed var panelDetent: PresentationDetent = .medium
    
    @Printed var showAlert: Bool = false
    
    func showAlertFromSheet() {
        // Make http request name and present an alert on failure
        self.showAlert = true
    }
}

Code for Customized Alert, incase when you want to refer/execute it:

extension View {
    func customAlert(isPresented: Binding<Bool>) -> some View {
        return modifier(CustomAlertView(isPresented: isPresented))
    }
}

struct CustomAlertView: ViewModifier {
    @Binding var isPresented: Bool
    
    init(isPresented: Binding<Bool>) {
        self._isPresented = isPresented
    }

    func physique(content material: Content material) -> some View {
        content material.fullScreenCover(isPresented: $isPresented) {
            alertView123.background(CustomAlertBackground())
        }
        .transaction { transaction in
            transaction.disablesAnimations = true
        }
    }
    
    non-public var alertView123: some View {
        GeometryReader { geometry in
            if self.$isPresented.wrappedValue {
                VStack {
                    Picture(systemName: "data.circle.fill").resizable().body(width: 32.0, peak: 32.0)
                        .padding(.high, 30).foregroundColor(Shade.blue)

                    Textual content("Alert Title").foregroundColor(Shade.major).font(.title2).daring().multilineTextAlignment(.middle)
                        .padding([.leading, .trailing], 20.0).padding(.high, 12.0)
                    Spacer()
                    
                    Textual content("Alert Message").foregroundColor(Shade.secondary).font(.physique).multilineTextAlignment(.middle)
                        .padding([.leading, .trailing], 20.0).padding(.high, 12.0)
                    Spacer()
                    
                    // Buttons
                }.fixedSize(horizontal: false, vertical: true).background(Shade.grey.opacity(0.5))
                .cornerRadius(28)
                .clipped()
                .padding([.leading, .trailing], 5.0)
                .place(x: geometry.measurement.width/2, y: geometry.measurement.peak/2)
                .body(width: 350.0)
            }
        }
    }
}

struct CustomAlertBackground: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        return InnerView()
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
    
    non-public class InnerView: UIView {
        override func didMoveToWindow() {
            tremendous.didMoveToWindow()
            superview?.superview?.backgroundColor = UIColor(Shade.grey.opacity(0.25))
        }
    }
}

With the code above, alert shouldn’t be getting offered and I get "Presently, solely presenting a single sheet is supported. The following sheet will probably be offered when the at present offered sheet will get dismissed." error in Xcode. I do not want to dismiss the sheet, however current a view (customized alert in my case) on high of it.

What’s one of the best ways to realize it as presenting the alert as full display screen cowl would not appear to be working?

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.