sanzaru/SimpleToast

Toast wont disappear

ch05enOne opened this issue ยท 9 comments

This library used to work for me but ever since I made some slight changes, once the toast appear, it never leaves. These are the options im using:

private let toastOptions = SimpleToastOptions(alignment: .bottom, hideAfter: 4, showBackdrop: false, backdropColor: Color.clear, animation: .easeInOut, modifierType: .slide)

Thanks for the info. Your options are fine, the bug was on my side.
I just released a version (0.2.1) that should fix your problem.

@sanzaru Thank you ๐Ÿ™ Great library btw

@sanzaru I think this should be closed as it's fixed now, no? :)

@sanzaru I think this should be closed as it's fixed now, no? :)

@Jeehut You're right. Just wanted to give some time to see the fix is working. ๐Ÿ˜‰

@sanzaru Unfortunately, the toast doesn't disappear for me anymore. I'm using version 0.2.2 on iOS 14.5. Quite a few things broke in 14.5 (e.g. with NavigationLink) so you may want to revisit this again. Here's my full modifier class, I'm always using it with :

import Design
import SwiftUI
import SimpleToast
import SFSafeSymbols

fileprivate struct Toast: ViewModifier {
  let isPresented: Binding<Bool>
  let icon: SFSymbol
  let message: LocalizedStringKey

  @Environment(\.colorScheme) var colorScheme

  func body(content: Content) -> some View {
    content
      .simpleToast(
        isShowing: isPresented,
        options: SimpleToastOptions(
          alignment: .bottom,
          hideAfter: 2.5, // <-- this should make it hide after 2.5 seconds, but it doesn't hide ...
          showBackdrop: false,
          animation: .linear,
          modifierType: .fade
        )
      ) {
        Label(message, systemSymbol: icon)
          .textStyle(.body(semibold: false, italic: false))
          .padding()
          .background(
            ZStack {
              VisualEffectView(effect: UIBlurEffect(style: .prominent))
              Color.blue.opacity(0.1)
            }
          )
          .foregroundColor(Color.blue.change(.luminance, to: colorScheme == .dark ? 0.66 : 0.33))
          .cornerRadius(10)
          .padding()
      }
  }
}

extension View {
  func toast(isPresented: Binding<Bool>, icon: SFSymbol, message: LocalizedStringKey) -> some View {
    modifier(Toast(isPresented: isPresented, icon: icon, message: message))
  }
}

@Jeehut I tried to reproduce your problem, but for me your code is working fine and the toast disappears as desired.

I built some simple view with your modifier (i just commented out some designs of yours and changed the icon definition):

issue-3

The problem you describe sounds like a problem with the binding you pass. On hiding the toast simply invalidates the timer, sets the binding back to false, the offset back to .zero and calls the completion function, if one was passed.

@sanzaru Thanks for the investigation. I will have a look into it, I just noticed while writing tests, that I'm actually receiving a new value for the binding, but the value is true. I'll report again if I find another issue within SimpleToast, otherwise you can assume I found the issue in my code and fixed it. ๐Ÿ‘

@sanzaru I actually couldn't find an issue with my Binding. I'm using TCA's binding creator which looks something like this:

.toast(
  isPresented: viewStore.binding(
    get: { $0.showForgotPasswordToast },
    send: LoginAction.setForgotPasswordToast(isPresented:)
  ),
  type: .warning,
  icon: .lockCircle,
  message: "TODO: Open screen 'forgot password'"
)

Just for the sake of testing my binding, I passed nil to the hideAfter option and instead added an onChange modifier with a simple delay function and it works now โ€“ so my feeling is that the binding isn't wrong, just something gets lost in how things are passed here to SimpleToast โ€“ maybe it's missing a strong reference to the binding?

Here's my updated code:

import Design
import SwiftUI
import SimpleToast
import SFSafeSymbols

fileprivate struct Toast: ViewModifier {
  let isPresented: Binding<Bool>
  let icon: SFSymbol
  let message: LocalizedStringKey

  @Environment(\.colorScheme) var colorScheme

  func body(content: Content) -> some View {
    content
      .simpleToast(
        isShowing: isPresented,
        options: SimpleToastOptions(
          alignment: .bottom,
          hideAfter: nil,
          showBackdrop: false,
          animation: .linear,
          modifierType: .fade
        )
      ) {
        Label(message, systemSymbol: icon)
          .textStyle(.body(semibold: false, italic: false))
          .padding()
          .background(
            ZStack {
              VisualEffectView(effect: UIBlurEffect(style: .prominent))
              Color.blue.opacity(0.1)
            }
          )
          .foregroundColor(Color.blue.change(.luminance, to: colorScheme == .dark ? 0.66 : 0.33))
          .cornerRadius(10)
          .padding()
      }
      .onChange(of: isPresented.wrappedValue) { value in
        if isPresented.wrappedValue {
          DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.5) {
            isPresented.wrappedValue = false
          }
        }
      }
  }
}

extension View {
  func toast(isPresented: Binding<Bool>, icon: SFSymbol, message: LocalizedStringKey) -> some View {
    modifier(Toast(isPresented: isPresented, icon: icon, message: message))
  }
}

@Jeehut
Thanks for the insights. I can only imagine that the binding of yours does not get propagated correctly to all parents. Have you tried using the SwiftUI property wrapper for debugging?