GithubHelp home page GithubHelp logo

exyte / popupview Goto Github PK

View Code? Open in Web Editor NEW
3.1K 3.1K 233.0 21.85 MB

Toasts and popups library written with SwiftUI

License: MIT License

Swift 98.86% Ruby 0.69% Objective-C 0.45%
popup swiftui swiftui-components swiftui-framework toast

popupview's People

Contributors

adusak avatar alex-m-b95 avatar andriizakhliupanyi avatar azaitsev avatar denis-obukhov avatar f3dm76 avatar gly avatar hossinasaadi avatar jlz avatar jpangburn avatar kaioelfke avatar mnndnl avatar mudrhs1997 avatar muratbataray avatar richardgroves avatar shipinev avatar villygreen avatar yonabalink avatar yonaharel avatar zapletnev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

popupview's Issues

Help regarding background blur and drop shadow

Hey everyone,

I was wondering if anybody could point me in the right direction on how to modify the PopupView.swift file to add a background blur and a dropshadow to the popup window. I have been trying to adjust the file but I'm unable to blur the view below the popup.

Thanks for your help!

how to pass custom Text to pop up

Hey all, I have Button which calls a ScannerView and receives the scanned barcode. I would like to pass the scanned String to a popup view and can't figure out how to do it properly. The pop up is always empty

That's my simplified code

struct WorkflowView: View {

    @State private var isShowingScanner = false
    @State private var scannedCode: String?
    @State private var isShowingScannerBanner = false

    var body: some View {
        ZStack{ //need to trigger pop up views
            VStack {
                    Button(action: {
                        self.isShowingScanner = true
                    }, label: {
                        Text("Scan barcode")
                            .font(.title)
                            .padding(5)
                            .background(Color.orange)
                            .cornerRadius(10)
                    })
            }
            .sheet(isPresented: $isShowingScanner) {
                    CodeScannerView(codeTypes: [.ean8, .ean13, .upce, .code39, .code93, .code128, .code39Mod43, .interleaved2of5, .qr]) { response in
                        if case let .success(result) = response {
                            scannedCode = result.string
                            isShowingScanner = false
                            isShowingScannerBanner = true
                        }
                    }
                }
        }
        .popup(isPresented: $isShowingScannerBanner, type: .floater(verticalPadding: 90), position: .top, autohideIn: 2) {
                    createSuccessBanner(message: scannedCode ?? "")
                }
}

Position .center?

Hi there. Thanks for this library. Could we also get a position .center? Thanks!

Popup tap to close & outside tap to close & AutoHide not working on dynamic parent view - Only DragView working

`
//MasterView.swift

import SwiftUI
import CoreData
import Foundation

class DataCenter: ObservableObject {
@published var toastPopupView:AnyView = AnyView(EmptyView())
@published var showtoastPopup:Bool = false
}

struct ContentView: View {

@StateObject var dataCenter = DataCenter()
@State var isLoading:Bool = false
var body: some View {
    ZStack{
       
        Color("launchScreenBackgroundColor").ignoresSafeArea()

        NavigationView{

            MainView(selectedView: ShowView()).environmentObject(dataCenter)
            
        }.navigationViewStyle(StackNavigationViewStyle())
       
            dataCenter.toastPopupView
   
    }.navigationBarHidden(true)
    .navigationBarBackButtonHidden(true)   
}

}
`

`
//ChildView.swift

import SwiftUI

struct DashboardS: View {

var body: some View {
    
    
    
    ZStack(alignment:.top){
        Color("launchScreenBackgroundColor2").ignoresSafeArea()
                                
    
    }.navigationBarHidden(true)
    .navigationBarBackButtonHidden(true)
    .onAppear {

]
dataCenter.showtoastPopup = false
dataCenter.toastPopupView = AnyView(
ZStack{

            }.frame(maxWidth: .infinity)
            .frame(height:120)
            .popup(isPresented:  $dataCenter.showtoastPopup, type: .toast, position: .bottom, animation: .default, autohideIn: 2, dragToDismiss: true, closeOnTap: true, closeOnTapOutside: true) {
                HStack {
                    Text("TEST").foregroundColor(Color.white)
                }
                .frame(maxWidth: .infinity)
                .frame(height:120)
                .background(Color.red)
                .shadow(color: .black, radius: 2)

            }.frame(maxWidth: .infinity)
            .frame(height:120)
            
        )
        DispatchQueue.main.async {
            dataCenter.showtoastPopup = true
        }

// DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// dataCenter.showtoastPopup = false. // this turning to false also not working
//// dataCenter.toastPopupView = AnyView(EmptyView())
// }

    }
  
    
   
}

}

`

this is a short example
but from main to a child there are several views that loads for bottom navigation.
popup comes, drag to close work but tap to close, outside tap to close and autohide not working.

custom tab View
Screenshot 2021-06-18 at 9 25 57 PM

popup load
Screenshot 2021-06-18 at 9 26 43 PM

View layers
Screenshot 2021-06-18 at 9 27 50 PM

Thanks

[BUG] dragToDismiss is ignored

Hi,

Many thanks for this great package :)

I found an issue (or so I think): the dragToDismiss parameter is not been considered. I believe it should be changed in the bellow function:

    /// This is the builder for the sheet content
    func sheet() -> some View {

        // if needed, dispatch autohide and cancel previous one
        if let autohideIn = autohideIn {
            dispatchWorkHolder.work?.cancel()
            
            // Weak reference to avoid the work item capturing the struct,
            // which would create a retain cycle with the work holder itself.
            dispatchWorkHolder.work = DispatchWorkItem(block: { [weak isPresentedRef] in
                isPresentedRef?.value.wrappedValue = false
                dismissCallback()
            })
            if isPresented, let work = dispatchWorkHolder.work {
                DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: work)
            }
        }

        let sheet = ZStack {
            Group {
                VStack {
                    VStack {
                        self.view()
                            .addTapIfNotTV(if: closeOnTap) {
                                self.dispatchWorkHolder.work?.cancel()
                                self.isPresented = false
                                self.dismissCallback()
                            }
                            .background(
                                GeometryReader { proxy -> AnyView in
                                    let rect = proxy.frame(in: .global)
                                    // This avoids an infinite layout loop
                                    if rect.integral != self.sheetContentRect.integral {
                                        DispatchQueue.main.async {
                                            self.sheetContentRect = rect
                                        }
                                    }
                                    return AnyView(EmptyView())
                                }
                            )
                    }
                }
                .frame(width: screenSize.width)
                .offset(x: 0, y: currentOffset)
                .animation(animation)
            }
        }

        // It should return the view if dragToDismiss is false
        if (!dargToDismiss) { return sheet }

        #if !os(tvOS)
        let drag = DragGesture()
            .updating($dragState) { drag, state, _ in
                state = .dragging(translation: drag.translation)
            }
            .onEnded(onDragEnded)

        return sheet
            .offset(y: dragOffset())
            .simultaneousGesture(drag)
        #else
        return sheet
        #endif
    }

Many thanks in advance.

popup toast doesn't appear if isPresented is .constant(true)

In this code:

            .popup(isPresented: .constant(true),
                   type: .toast,
                   position: .bottom,
                   dragToDismiss: true,
                   closeOnTap: false,
                   closeOnTapOutside: true,
                   dismissCallback: {
                   // on dismiss
                  }) {
                CardView { ... }

PopupView bases the appearAction on isPresented changing:

        .valueChanged(value: isPresented) { isPresented in
            appearAction(isPresented: isPresented)
        }

But if the binding is a .constant(true) since PopupView's inception, it doesn't appear.

Keep getting "Invalid frame dimension" error

When dismissing the popup view, got error message often, anyone know how to resolve it?

2021-04-14 12:51:35.864282+0800 gikiapp[923:160335] [SwiftUI] Invalid frame dimension (negative or non-finite).
2021-04-14 12:51:35.883202+0800 gikiapp[923:160335] [SwiftUI] Invalid frame dimension (negative or non-finite).

Can not hide on Mac Catalyst

.popup(isPresented: $showAlert, type: .floater() ,position: .top, animation: Animation.spring(), autohideIn: 2) {
            HStack(spacing: 10) {
                Image(systemName: "xmark.circle.fill")
                    .foregroundColor(.white)
                    .font(.system(size: 20))
                Text("No data".localized)
                    .font(.headline)
                    .foregroundColor(.white)
            }
            .frame(width: 150, height: 40)
            .background(Color.red)
            .cornerRadius(30.0)
            .shadow(radius: 5)
        }

image

Suggestion: Add opacity to body content

Hello there.

When I use both ColorPicker & PopupView, ColorPicker tap gesture get blocked by PopupView.

I added these code to body content โฌ‡๏ธ fixed this problem:

.opacity(isPresented ? 1 : 0)

ๆˆชๅฑ2021-07-18 ไธ‹ๅˆ6 29 59

Xcode 12 build failure

I can see this error in new XCode 12

The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

image

Feature Request: Queue

First off I'm happy with the library, it made showing a popup easy. I'd very much like to queue them and have found that to be somewhat cumbersome on my end. Any chance you could add either some kind of dismiss callback, make the binding value passed in set to false when autohide happens, or just build some nice API for queuing popups?

[Bug]Fix retain cycle issue

First, thanks for your PopupView libraryโค๏ธ.

Issue

ContentView's @StateObject variable never deinit

Minimal Example

class Test: ObservableObject {
    var loadState: EMLoadState = .loading
    var groupEntityArr: [ListGroupEntity] = []
    var groupStructArr: [ListGroupStruct] = []
    @Published var groupUIArr: [ListGroupUI] = []
    
    deinit {
        print("deinit will never be executed") // ใ€Š=========
    }
}
struct TestView: View {
    @StateObject var viewModel = Test()
    @State var show: Bool = false
    @State var text: String = ""
    var body: some View {
        ZStack {
            
        }
        .popup(isPresented: $show, autohideIn: 2, dismissCallback: {
            temp()
        }, view: {
            Text(text)
        })
    }
    
    func temp() {
        print(self.text)
    }
}

Solution

if let autohideIn = autohideIn {
            dispatchWorkHolder.work?.cancel()
            
            let block = dismissCallback // add this code
            dispatchWorkHolder.work = DispatchWorkItem(block: { [weak isPresentedRef] in
                isPresentedRef?.value.wrappedValue = false
                dismissCallback() // remove this code, which cause @StateObject never deinit
                block() // add this code
            })
            if isPresented, let work = dispatchWorkHolder.work {
                DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: work)
            }
        }

show popup on tabbar page

Hello there.
I want to show pop ups on tabbar page. I want to show the views I have created in subpages but. I want to send views via shared class ObservableObject. I couldn't solve it anyway. Can you help me with this?


class sharedValues: ObservableObject {
    @Published var selectedItemIndex : Int = 1
    @Published var showTabBar : Bool = true
    @Published var blurTabBar : Bool = false
    
    
    
    @Published var showingPopup = false

    var sharePopupView ?
}




struct TabPage: View {
    @EnvironmentObject var share : sharedValues

    var body: some View {
        VStack(spacing:0) {

            NavigationView{
                
                switch share.selectedItemIndex {
                case 0: Shop()
                case 1: ConversationsView()

                case 2: GameHome()
                case 3: GameHome()
                default: GameHome()
                }
            }
            
            Tabbar()
                
        }
        .popup(isPresented: $share.showingPopup, type: .`default`,animation: .spring(), closeOnTapOutside: true) {
            share.sharePopupView
        }

    }
    
}


struct ConversationsView: View {
    @ViewBuilder private func sunpage1() -> some View {
        HStack(){
                Button(action: {
                    share.showingPopup = true
                    share.sharePopupView = {
                        Some view items ????
                    }
                }) {
                    VStack {
                        Image(systemName: "trash.slash")                        
                        Text("delete all")
                    }
                }
}
}

Animates in wrong direction for top / bottom

Currently, when I go for a toast or a float, if I pass .top, it animates from the top bot goes all the way to the bottom. If I pass in .bottom, it animates from the bottom but goes all the way to the top

Improvement: Fix sheet layout bugs & NavigationBarItem layout bugs

Hello there!

I modified the source code, and fixed these issues:

1ใ€Wrong layout in presented view(present via sheet)
2ใ€NavigationBarItem bounce when pull down a presented view(view.isModalInPresentation = true)
3ใ€Popup block ColorPicker tap gesture when popup view is invisible.

Which I didn't implement:

1ใ€Have not implement drag gesture
2ใ€Have not test on Macใ€Watchใ€TV

Apologize for no time to pull a request.
Thanks for your open source.

// Usage
struct TestPage: View {
    @State var show: Bool = false
    var body: some View {
        VStack {
            Text("1")
                .onTapGesture {
                    show.toggle()
                }
            Spacer()
        }
        .em_popup(isPresented: $show,
                  position: .top(padding: 44),
                  duration: 2,
                  animation: .spring(),
                  ignoreEdges: nil,
                  closeOnTap: true,
                  closeOnTapOutside: true,
                  popupContent: {
                    Color.red.frame(width: 200, height: 100)
                  }, onDismiss: {
                    print("dismissed")
                  })
    }
}
// Source Code
import SwiftUI

extension View {
    
    public func em_popup<EMPopupContent: View>(isPresented: Binding<Bool>,
                                               position: EMPopupPosition,
                                               duration: Double? = nil,
                                               animation: Animation? = .spring(),
                                               ignoreEdges: Edge.Set?,
                                               closeOnTap: Bool = true,
                                               closeOnTapOutside: Bool = true,
                                               popupContent: @escaping () -> EMPopupContent,
                                               onDismiss: @escaping() -> ()) -> some View {
        self.modifier(
            EMPopupView(isPresented: isPresented,
                        position: position,
                        duration: duration,
                        animation: animation,
                        ignoreEdges: ignoreEdges,
                        closeOnTap: closeOnTap,
                        closeOnTapOutside: closeOnTapOutside,
                        popupContent: popupContent,
                        onDismiss: onDismiss)
        )
    }
    
    @ViewBuilder
    func em_apply_if<T: View>(_ condition: Bool, apply: (Self) -> T) -> some View {
        if condition {
            apply(self)
        } else {
            self
        }
    }
}


public enum EMPopupPosition: Equatable {
    case top(padding: CGFloat = 0)
    case bottom(padding: CGFloat = 0)
    
    var padding: CGFloat {
        switch self {
        case let .top(padding):
            return padding
        case let .bottom(padding):
            return padding
        }
    }
    
    static public func == (lhs: EMPopupPosition, rhs: EMPopupPosition) -> Bool {
        switch lhs {
        case .top:
            switch rhs {
            case .top:
                return true
            case .bottom:
                return false
            }
        case .bottom:
            switch rhs {
            case .top:
                return false
            case .bottom:
                return true
            }
        }
    }
}


public struct EMPopupView<EMPopupContent: View>: ViewModifier {
    
    @Binding var isPresented: Bool
    
    var position: EMPopupPosition
    
    var duration: Double?
    
    var animation: Animation?

    var ignoreEdges: Edge.Set?
    
    var closeOnTap: Bool
    
    var closeOnTapOutside: Bool

    var popupContent: () -> EMPopupContent
    
    var onDismiss: () -> ()
    

    @State private var presenterContentRect: CGRect = .zero
    
    @State private var popupContentRect: CGRect = .zero

    @State private var topPadding: CGFloat = 0

    private var dispatchWorkHolder: DispatchWorkHolder = DispatchWorkHolder()

    // Handle dispatch capture self
    private var isPresentedRef: ClassReference<Binding<Bool>>?
    
    private var hideOffset: CGFloat {
        if case EMPopupPosition.top(_) = position {
            return -popupContentRect.height
        } else {
            return presenterContentRect.height
        }
    }
    
    init(isPresented: Binding<Bool>,
         position: EMPopupPosition,
         duration: Double?,
         animation: Animation?,
         ignoreEdges: Edge.Set?,
         closeOnTap: Bool,
         closeOnTapOutside: Bool,
         popupContent: @escaping () -> EMPopupContent,
         onDismiss: @escaping() -> ()) {
        self._isPresented = isPresented
        self.position = position
        self.duration = duration
        self.animation = animation
        self.ignoreEdges = ignoreEdges
        self.closeOnTap = closeOnTap
        self.closeOnTapOutside = closeOnTapOutside
        self.popupContent = popupContent
        self.onDismiss = onDismiss
        self.isPresentedRef = ClassReference(self.$isPresented)
    }
    
    public func body(content: Content) -> some View {
        content
            .background(
                // get presenter' rect
                GeometryReader { proxy -> AnyView in
                    let rect = proxy.frame(in: .global)
                    if rect.integral != self.presenterContentRect.integral {
                        DispatchQueue.main.async {
                            self.presenterContentRect = rect
                        }
                    }
                    return AnyView(EmptyView())
                }
            )
            .overlay(makePopupContent())
    }
    
    private func makePopupContent() -> some View {
        if duration != nil {
            dispatchWorkHolder.work?.cancel()
            dispatchWorkHolder.work = DispatchWorkItem(block: { [weak isPresentedRef] in
                isPresentedRef?.value.wrappedValue = false
                onDismiss()
            })
            if isPresented && dispatchWorkHolder.work != nil {
                DispatchQueue.main.asyncAfter(deadline: .now() + duration!, execute: dispatchWorkHolder.work!)
            }
        }
        
        let popup = ZStack {
            // background
            Color.clear
                .onTapGesture {
                    if closeOnTapOutside {
                        self.dispatchWorkHolder.work?.cancel()
                        isPresented = false
                        self.onDismiss()
                    }
                }

            // pop up content
            VStack(spacing: 0) {
                if position == EMPopupPosition.bottom(padding: 0) { Spacer() }
                HStack(spacing: 0) {
                    popupContent()
                        .padding(.top, position == EMPopupPosition.top(padding: 0) ? position.padding : 0)
                        .padding(.bottom, position == EMPopupPosition.bottom(padding: 0) ? position.padding : 0)
                        .background(
                            GeometryReader { proxy -> AnyView in
                                let rect = proxy.frame(in: .global)
                                if rect.integral != self.popupContentRect.integral {
                                    DispatchQueue.main.async {
                                        self.popupContentRect = rect
                                    }
                                }
                                return AnyView(EmptyView().frame(width: popupContentRect.width).animation(.none))
                            }
                        )
                        .onTapGesture {
                            if closeOnTap {
                                self.dispatchWorkHolder.work?.cancel()
                                isPresented = false
                                self.onDismiss()
                            }
                        }
                }
                
                if position == EMPopupPosition.top(padding: 0) { Spacer() }
            }
        }
        .em_apply_if(ignoreEdges != nil, apply: { view in
            view.edgesIgnoringSafeArea(ignoreEdges!)
        })
        .opacity(isPresented ? 1 : 0)
        .offset(y: isPresented ? 0 : hideOffset)
        .animation(animation)

        return popup
            
    }
}

// Handle dispatch capture self
fileprivate class DispatchWorkHolder {
    var work: DispatchWorkItem?
}

// Handle dispatch capture self
fileprivate final class ClassReference<T> {
    var value: T
    
    init(_ value: T) {
        self.value = value
    }
}

Redrawing of views

Changing how the view is shown would prevent reloading of the content.

public func body(content: Content) -> some View {
        Group {
            main(content: content)
        }
        .valueChanged(value: isPresented) { isPresented in
            appearAction(isPresented: isPresented)
        }
    }
    
    private func main(content: Content) -> some View {
        ZStack {
            content
                .frameGetter($presenterContentRect)
            
            if showContent {
                backgroundColor
                    .applyIf(closeOnTapOutside) { view in
                        view.contentShape(Rectangle())
                    }
                    .addTapIfNotTV(if: closeOnTapOutside) {
                        dismiss()
                    }
                    .edgesIgnoringSafeArea(.all)
                    .opacity(currentBackgroundOpacity)
                    .animation(animation)
            }
        }
        .overlay(sheet())
    }

closeOnTapOutside causes issues with SegmentedPicker

Hi,
I have found that if the view being presented by PopupView contains a SegmentedPicker view and closeOnTapOutside is true, the SegmentedPicker does not respond to changes with the usual tap gesture, only to long presses and dragging of segments across it. I believe there are issues with the TapGesture responsible for closeOnTapOutside.

Example with closeOnTapOutside set to true
https://user-images.githubusercontent.com/17231825/127427851-838b4b1c-99ce-490a-9085-29199ddb0b19.mov

Example with closeOnTapOutside set to false

RPReplay_Final1627529799.mov

Need to get dismiss source

I want to react differently when popup is dismissed because of a tap or because of a drag.

Here is the changes I've made to accomplish this ๐Ÿ‘‡

PopupChanges

Is there another way of achieving what I want ?
I tried adding a click on my view but it doesn't work well since it doesn't take into account your drag handling code.

Do you think that can interest other users ? Do you want me to do a pull request ?

NavigationView makes the app crash with SIGABRT

I've tested the Popup with this simple View

import SwiftUI
import PopupView

struct ContentView: View {
    
    @State var name: String = ""
    @State var isPopupPresented = false
    
    var body: some View {
        NavigationView {
            ZStack {
                Text("Test")
                    .padding()
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke().foregroundColor(.green))
                
            }.popup(isPresented: $isPopupPresented, view: {
                HStack {
                    Text("The popup")
                }
                .frame(width: 200, height: 60)
                .background(Color(red: 0.85, green: 0.8, blue: 0.95))
                .cornerRadius(30.0)
            })
            
            .navigationBarTitle("Large Title")
        }
    }
}

but as soon as I deploy it on the simulator the view crashes with this log

2020-06-11 12:07:41.661664+0200 Test[2114:73858] precondition failure: invalid input index: 4
(lldb) 

Screenshot 2020-06-11 at 12 10 05

closeOnTapOutside doesn't work as intended

I'm using this view like this:

var body: some View {
    ZStack {
        VStack(alignment: .center, spacing: 0, content: {

            Text("Hello")

            Spacer().frame(height: 50)

            Text("Another Text")

            Spacer().frame(height: 30)

            SomeOtherComponent()
            
            Button("Next") {
                print("Button clicked")
            }

            Spacer()

            Image("Official-Logo")
                .resizable()
                .scaledToFit()
                .padding(.vertical, 10)
                .padding(.horizontal, 15)
                .frame(
                    minWidth: 0,
                    maxWidth: .infinity
                )

        }).padding(.top, 50)
    }.popup(isPresented: $showingMessage, closeOnTapOutside: true) {
        VStack(alignment: .leading, spacing: 5, content: {
            Text("Information?")
                .padding(EdgeInsets(top: 10, leading: 10, bottom: 5, trailing: 10))

            Text("Lorem Ipsum Dolor.")
                .padding(EdgeInsets(top: 5, leading: 10, bottom: 20, trailing: 10))
        })
        .frame(width: 320, height: 250)
        .background(Color("darkGray"))
        .cornerRadius(5.0)
    }
}

So in this view, whenever I click outside the popup esp. in the spacers it just fails to dismiss the popup.

Improvement: Drag to dismiss

Hi ! Thanks for your work and for sharing with us this library. I'm coming with a suggestion for improving the UX regarding the options that a user has in order to dismiss the popup. It would be cool if the user would be able to dismiss it by dragging down. Thanks a lot !

add default value for floater's vertical padding

When I use the type .floater(verticalPadding:) in case of a NavigationView and the position is top, the popup view appears on top of the notch in iPhone X and later, which requires me to calculate safeAreaInsets.top and then add it to the padding, this can be done using GeometryReader which's not suitable in some situations and has side effects.
I believe adding a default value for the padding to include top safe area insets would be a good idea.

Is there a way to make the toast show on top of modals?

Ideally we'd like to be able to display a toast and it appear no matter if there was a modal on the screen or not. Right now if there is a modal the toast appears underneath it. We could add the view modifier onto this modal view, but then the toast would go down once the view was dismissed. Is this something that is possible?

autoHide might be failed in Xcode 13

Description

  • autohideIn is not working: the dismissCallback will be called continuously, and the dialog is not dismissed
.popup(isPresented: $viewModel.infoToastShow,
                       type: .floater(verticalPadding: 15),
                       position: .top,
                       autohideIn: 2,
                       dismissCallback: viewModel.infoToastCallback) {
                           // UI
                       }

Xcode

Screen Shot 2021-10-06 at 11 38 15 PM

Popup view makes the child view navigate to top

Xcode. 13.1
iOS 15.0

At the end of a long list I want to trigger a popup view. When the user triggers it the scrollview is reset to beginning.

struct ChildView: View {
    
    @Binding var show: Bool
    let text: String
    
    var body: some View {
        
        ScrollView(.vertical, showsIndicators: false) {
            ForEach(0..<50) { _ in
                Text("Hello World")
                    .padding()
            }
            
            VStack {
                Button {
                    show.toggle()
                } label: {
                    Text(text)
                }
            }
        }
        .clipped()

    }
}

struct TabsView: View {
    
    @State var show = false
    
    var body: some View {
        
        ZStack {
            TabView {
                ChildView(show: $show, text: "FirstView")
                    .tabItem({
                        Label("First", systemImage: "greetingcard")
                    })
            }
        }
        .popup(isPresented: $show, type: .default, dragToDismiss: true, closeOnTap: false, closeOnTapOutside: true, backgroundColor: .black.opacity(0.5)) {
            VStack {
                Text("๐Ÿ‘ป")
                Text("Hello World.")
                Button {
                    show.toggle()
                } label: {
                    Text("Dismiss ")
                }
            }
            .padding(.vertical, 20)
            .padding(.horizontal, 40)
            .frame(width: 300, height: 300)
            .background(.red)
            .cornerRadius(10)
        }
    }
}

Ignore NavigationBar in Popup

popup with background not ignore navigationview, is it possible to cover all screen while keep using navigationview ?

Screen Shot 2022-04-01 at 16 06 29

Popup not showing up

The popup is not working anymore, it doesn't appear ๐Ÿค” this code was working in v0.0.3 but not on the latest v0.0.4

struct ContentView: View {
    
    @State var name: String = ""
    @State var isPopupPresented = false
    
    var body: some View {
        ZStack {
            VStack {
                Text("Test")
                    .padding()
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke().foregroundColor(.green))
                
                Button(action: {
                    self.isPopupPresented.toggle()
                }, label: { Text("Deploy").padding() })
            }.padding()
            
        }.popup(isPresented: $isPopupPresented, type: .toast, autohideIn: 3.0, view: {
            HStack {
                Text("The popup")
            }
            .frame(width: 200, height: 60)
            .background(Color(red: 0.85, green: 0.8, blue: 0.95))
            .cornerRadius(8.0)
            .padding(.bottom, 25)
        })
    }
}

Popup blocks touch propagation to List View

When using this library with a List and Navigation links, you have to hold the list cell to activate the navigation link or swipe to delete.

Here is the code I'm using to set up the toast:

.popup(isPresented: $showCopiedToast,
                   type: .toast,
                   position: .bottom,
                   autohideIn: 2,
                   closeOnTap: false) {
                HStack {
                    Text(self.toastText)
                }
                .frame(width: 200, height: 60)
                .background(Color(red: 0.85, green: 0.8, blue: 0.95))
                .cornerRadius(30.0)
                .padding(15)
            }

My guess is the simultaneousGesture stuff is messing with the list view. A workaround or fix would be appreciated.

Add .popup(item:) variant

There's a need to pass information to the toast UI. SwiftUI handles this for various views by providing an item variant for view modifiers:

    public func popover<Item, Content>(item: Binding<Item?>, attachmentAnchor: PopoverAttachmentAnchor = .rect(.bounds), arrowEdge: Edge = .top, @ViewBuilder content: @escaping (Item) -> Content) -> some View where Item : Identifiable, Content : View

I'm looking at your code and wondering how hard it would be to add the same thing, but I don't quite understand everything you're doing in yours.

Tap to dismiss doesn't cancel previously scheduled auto hide DispatchWorkItem

I've implemented a queue of toasts, using PopupView for presentation. The issue I'm running into is that when a toast is dismissed via tap and I present the next toast in queue, it gets auto-hidden early. It looks like this is the code with the bug.

if let autohideIn = autohideIn {
    dispatchWorkHolder.work?.cancel()
    dispatchWorkHolder.work = DispatchWorkItem(block: {
        self.isPresented = false
    })
    if isPresented, let work = dispatchWorkHolder.work {
        DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: work)
    }
}

It seems that since dispatchWorkHolder is a class, it's work property is being passed by reference. Therefore, when I present a second toast, the work property is set to a new DispatchWorkItem that has not been cancelled. So at the time the previously scheduled asyncAfter executes, it is not a cancelled item.

Prevent the tap event bubbling

Hi, Thanks for your works on this cool project, I have question that when we enable closeOnTapOutside , how can enable prevent the outside view object receiving the tap event?

Popup view should be lazy loaded

Here is an example

The current implemention is even showSystemAlertNotification = false, popup body is still executed. This could bring some problems in reality:

data struct is set a valid value only if showSystemAlertNotification= true. Since it's not lazy-loaded, a crash happens since self.systemAlertNotification = nil

.popup(isPresented: $showSystemAlertNotification, type: .floater(), position: .top, animation: Animation.spring()) {
                SystemAlert(alert: self.data!)
            }

Close Modal When Click to Outside

Hello, thanks for this useful pod :)

I recommend if you add a feature for hiding modal when users click to the outside of modals, it will be so useful :)

Thanks in advance.

Top Floater is showing a line after dismissed

I just cloned the repository and tried the example but it seems top floater is leaving a line after it gets dismissed.
All other popup types doesn't show it.

I am using Xcode 13.3.
In between thanks for making this great library.

Limitation usage

PopouView .popup requires a host view to be a fullscreen. For example, if I put .popup at a view with height =200, it will show a different way.
Is there any better way to allow it to attach to any view and show at the correct position?

Simulator Screen Shot - iPhone 11 Pro Max - 2020-05-21 at 10 02 13

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.