Mijick / PopupView

Popups presentation made simple (SwiftUI)
MIT License
1.08k stars 42 forks source link

Animation Glitch on FullScreen Implementation. #97

Closed dentvii closed 1 month ago

dentvii commented 2 months ago

First of all, thank you for the amazing package!

I'm encountering an issue when using popups in combination with views that contain .edgesIgnoringSafeArea(.all). Specifically, I've noticed that when the popup has contentIgnoresSafeArea set to either true or false, the following glitches occur:

  1. Animation Glitch:

    • When the popup animation goes from bottom to top, it stops at the safe area boundary before continuing. This results in a noticeable hitch in the animation.
  2. Post-Dismissal Lag:

    • After dismissing the popup, there is an animation lag where a thin component of the view remains visible momentarily.

I would greatly appreciate any assistance in resolving this issue.


struct WebViewWrapperView: View {

    @Environment(\.presentationMode) private var presentation

    let title : String
    let request: URLRequest

    var body: some View {
        VStack {
            navigationBar

            titleView

            WebView(request: request)
        }
    }
    private var titleView : some View {
        HStack{
            Text(title)
                .modifier(Manrope40Bold())
                .fixedSize(horizontal: false, vertical: true)
                .foregroundColor(Color.black)
                .padding(.bottom, 12)
            Spacer()
        }.padding(.horizontal)
    }

    private var navigationBar : some View {
        return HStack {
            Button(action: {
                self.presentation.wrappedValue.dismiss() // dismiss the navigation view
                PopupManager.dismiss()
            }, label: {
                RoundButtonStyleView(buttonType: .backArrow, widthAndHeight: 36, tintColor: .gray, iconColor: .black)

            })
            Spacer()

        }
        .padding(.horizontal)
        .padding(.top, 60)
    }
}

import SwiftUI
import MijickPopupView

struct PopupFullScreen<Content: View>: BottomPopup {
    var cornerRadius: CGFloat?
    var contentIgnoresSafeArea: Bool?
    var horizontalPadding: CGFloat?
    var backgroundColor: Color?
    var content: () -> Content

    init(
        cornerRadius: CGFloat? = nil,
        contentIgnoresSafeArea: Bool? = nil,
        horizontalPadding: CGFloat? = nil,
        backgroundColor: Color? = nil,
        @ViewBuilder content: @escaping () -> Content
    ) {
        self.content = content
        self.cornerRadius = cornerRadius
        self.contentIgnoresSafeArea = contentIgnoresSafeArea
        self.horizontalPadding = horizontalPadding
        self.backgroundColor = backgroundColor
    }

    func configurePopup(popup: BottomPopupConfig) -> BottomPopupConfig {
        var modifiedPopup = popup
            .contentFillsEntireScreen(true)
            .dragGestureEnabled(false)
        if let cornerRadius = cornerRadius {
            modifiedPopup = modifiedPopup.cornerRadius(cornerRadius)
        }
        if let contentIgnoresSafeArea = contentIgnoresSafeArea {
            modifiedPopup = modifiedPopup.contentIgnoresSafeArea(contentIgnoresSafeArea)
        }
        if let horizontalPadding = horizontalPadding {
            modifiedPopup = modifiedPopup.horizontalPadding(horizontalPadding)
        }
        if let backgroundColor = backgroundColor {
            modifiedPopup = modifiedPopup.backgroundColour(backgroundColor)
        }
        return modifiedPopup
    }

    func createContent() -> some View {
        content()
            .frame(maxHeight: .infinity, alignment: .top)
    }
}
 private func configurePopup(_ config: GlobalConfig) -> GlobalConfig { config
        .top { $0
            .cornerRadius(24)
            .dragGestureEnabled(true)
            .tapOutsideToDismiss(true)
        }
        .centre { $0
            .tapOutsideToDismiss(false)
        }
        .bottom { $0
            .stackLimit(4)
            .tapOutsideToDismiss(true)
        }
    }

Simulator Screen Recording - Traft - iPhone 14 PM 16 4 - 2024-05-18 at 23 07 18 Simulator Screen Recording - Traft - iPhone 14 PM 16 4 - 2024-05-18 at 23 09 55

FulcrumOne commented 2 months ago

Hey @dentvii!

Thank you for these words and this amazing description, it will help us a lot in understanding the source of the problem!

Regarding the first point, I would advise delaying the loading of the content of the popup (0.5 seconds should be enough). Here you have an example how you can achieve it:

(...)
@State private var showContent: Bool = false
(...)

    func createContent() -> some View {
        (...)
        .animation(.easeInOut, value: showContent)
        .onAppear() {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { showContent = true }
        }
    }

    (...)

    @ViewBuilder func createWebView() -> some View { if showContent {
        WebView()
    }}

    (...)

This problem occurs because both WKWebView and StoreKit are extremely CPU intensive (especially if there's nothing in cache memory yet).

Regarding the second problem, thank you for pointing it out, I thought it had already been fixed, I will fix it later this week (sorry for the delay, but we are busy preparing an update for Navigattie, our second library).

Once again, I would like to thank you for these discoveries and have a great day!

dentvii commented 2 months ago

Thank you for your quick response and your interest in fixing this issue!

I believe the problem is related to the edgesIgnoreSafeArea property. Here’s how you can reproduce the views and observe the issue:


fileprivate struct MockSubscriptionAlternative01View: View {

    var body: some View {
        ZStack {
            ImageBackground()
            LinearGradientView()

            ScrollView(.vertical, showsIndicators: false) {
                VStack {
                    CloseButtonView()
                    Spacer()
                }.padding(.horizontal, 30)
            }

        }
        .edgesIgnoringSafeArea(.all)
    }
}

fileprivate struct LinearGradientView: View {
    var body: some View {
        LinearGradient(gradient: Gradient(colors: [Color.blue.opacity(0.4), Color.blue, Color.blue]), startPoint: .top, endPoint: .bottom)
    }
}

fileprivate struct ImageBackground: View {
    var body: some View {
        VStack {
            Image("subscription01")
                .resizable()
                .scaledToFit()
                .border(Color.red)
                .scaleEffect(1.4)
                .offset(x: 70, y: 0)
            Spacer()
        }
    }
}

fileprivate struct CloseButtonView: View {
    var body: some View {
        HStack {
            Button(action: {
                PopupManager.dismiss()
            }) {
                RoundButtonStyleView(buttonType: .close, widthAndHeight: 36, tintColor: Color.white.opacity(0.1), iconColor: Color.white.opacity(0.6))
                    .shadow(color: Color.black.opacity(0.5), radius: 4)
                    // .background(Color.black.clipShape(Circle()).opacity(0.3))
            }

            Spacer()
        }
        .padding(.top, 50)
    }
}

fileprivate struct PopupDemoView: View {
    var body: some View {
        VStack (spacing: 10) {
            Button("Top", action: {
                PopupTopNotification(horizontalPadding: 20) {
                    VStack {
                        Color.red
                    }
                }.showAndStack()
            })

            Button("Center", action: {
                PopupCenter (horizontalPadding: 20) {
                    VStack {
                        Text("Unlock New Features")
                            .foregroundColor(.black)
                            .multilineTextAlignment(.center)
                            .fixedSize(horizontal: false, vertical: true)

                        Text("Proident adipisicing labore consectetur tempor eiusmod ullamco enim adipisicing eiusmod.")
                            .foregroundColor(.black)
                            .multilineTextAlignment(.center)
                            .fixedSize(horizontal: false, vertical: true)

                        HStack {
                            Text("Cancel")
                                .foregroundColor(.black)
                                .frame(height: 46)
                                .frame(maxWidth: .infinity)
                                .cornerRadius(8)

                            Text("Unlock")
                                .foregroundColor(.black)
                                .frame(height: 46)
                                .frame(maxWidth: .infinity)
                                .cornerRadius(8)
                        }
                    }
                }.showAndStack()
            })
            Button("Full screen Mock Subscription", action: {
                PopupFullScreen {
                    MockSubscriptionAlternative01View()
                        .onTapGesture {
                            PopupManager.dismiss()
                        }
                }.showAndStack()
            })
            Button("Full screen", action: {
                PopupFullScreen {
                    Color.green                        .onTapGesture {
                            PopupManager.dismiss()
                        }
                }.showAndStack()
            })
            Button("Sheet", action: {
                PopupSheet (backgroundColor: Color.white){
                    VStack {

                        Capsule()
                            .fill(Color.gray)
                            .frame(width: 32, height: 6)
                            .padding(.vertical)

                        Color.green
                    }
                        .onTapGesture {
                            PopupManager.dismiss()
                        }
                }.showAndStack()
            })
            Button("Sheet", action: {
                PopupSheet (contentFillsWholeHeigh: false){
                    VStack {
                        Text("Unlock New Features")
                            .foregroundColor(.black)
                            .multilineTextAlignment(.center)
                            .fixedSize(horizontal: false, vertical: true)

                        Text("Proident adipisicing labore consectetur tempor eiusmod ullamco enim adipisicing eiusmod.")
                            .foregroundColor(.black)
                            .multilineTextAlignment(.center)
                            .fixedSize(horizontal: false, vertical: true)
                    }.padding()
                        .onTapGesture {
                            PopupManager.dismiss()
                        }
                }.showAndStack()
            })
            Button("Bottom Notification", action: {
                PopupBottomNotification {
                    Color.green.frame(50)
                        .onTapGesture {
                            PopupManager.dismiss()
                        }
                }.showAndStack()
            })
        }
        .background(Color.traftRedLight)
    }
}

fileprivate struct CustomPreviewProviderView : View {
    @State var theme: Theme  = .Light

    init(theme: Theme = .Light) {
        self.theme = theme
    }

    var body: some View {
        ZStack {
            Color.blue
            PopupDemoView()
        }
        .implementPopupView(config: configurePopup)
        .environmentObject(themeManager)
    }

    func configurePopup(_ config: GlobalConfig) -> GlobalConfig { config
        .top { $0
            .cornerRadius(24)
            .dragGestureEnabled(true)
            .tapOutsideToDismiss(true)
        }
        .centre { $0
            .tapOutsideToDismiss(true)
        }
        .bottom { $0
            .stackLimit(4)
            .dragGestureEnabled(true)
            .tapOutsideToDismiss(true)
        }
    }

}

struct PopupDemoView_Previews: PreviewProvider {
    static var previews: some View {
        CustomPreviewProviderView()
    }
}

FullScreenEdgesBug

FulcrumOne commented 2 months ago

Thanks @dentvii

FulcrumOne commented 2 months ago

@dentvii, the problem should be resolved with patch-2.4.2 branch. To make it work, please delete ignoresSafeArea from your MockSubscriptionAlternative01View structure and declare your popup as:

PopupFullScreen(contentIgnoresSafeArea: true) {
                    MockSubscriptionAlternative01View()
                        .onTapGesture {
                            PopupManager.dismiss()
                        }
                }.showAndStack()

Uploading CleanShot 2024-05-23 at 14.30.46.mp4…

Thanks again for the finding and apologies, but there will be a slight delay in other issues as some unpredictable things I have to deal with now have happened in my regular work 😉