-
Notifications
You must be signed in to change notification settings - Fork 752
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create a formal dismissal mechanism for SwiftUI message #549
Comments
I'm running into this issue now, and would like to upvote this feature! I've created a (Model) public struct Banner: Equatable, Identifiable {
public let style: Style
public let title: LocalizedStringKey
public let description: LocalizedStringKey?
public let image: Image?
public let action: Banner.Action?
} (View) extension Banner: MessageViewConvertible {
public func asMessageView() -> some View {
BannerView(banner: self)
.padding(.large)
.padding(.vertical, .huge + .large)
}
} As always, thank you so much for all of the hard work. |
The recommended solution for dismissal is to use the view builder version of the struct ContentView: View {
private struct Message: Equatable, Identifiable {
var text: String
var id: String { text }
}
@State private var message: Message?
var body: some View {
Button("Tap") {
message = Message(text: "Testing")
}
.swiftMessage(message: $message) { message in
// The message view
VStack {
Text(message.text)
Button("OK") {
self.message = nil
}
.buttonStyle(.bordered)
}
.padding(50)
.background(.yellow)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
}
} The use of |
Thanks for the suggestion @wtmoose! As you requested, below is a rough version of how displaying notifications in my app works currently. I'm trying to think of the best way to port this over to the modifier style, with some constraints I have in the way I built this for the pre-SwiftUI approach. Right now the solution is rather decoupled, or I should say, the main dependency is a singular The public extension BannersController {
func present(_ banner: Banner, direction: BannerDirection = .bottom, duration: BannerDuration = .automatic) {
// We use this variant of SwiftMessages.show to ensure that the View is properly
// dispatched onto the main queue, guarantees not provided by other variants.
SwiftMessages.show(config: self.configuration(direction: direction, duration: duration)) {
MessageHostingView(message: banner)
}
}
func dismiss() {
SwiftMessages.hide()
}
} That public struct Banner: Equatable, Identifiable {
public let style: Style
public let title: LocalizedStringKey
public let description: LocalizedStringKey?
public let image: Image?
public let action: Banner.Action?
public init(style: Style, title: LocalizedStringKey, description: LocalizedStringKey?, image: Image?, action: Banner.Action?) {
self.style = style
self.title = title
self.description = description
self.image = image
self.action = action
}
public var id: String {
UUID().uuidString
}
}
public extension Banner {
enum Style {
case success
case info
case accent
case error
}
} Eschewing some details for the sake of space. public struct BannerView: View {
@Environment(\.preferredColorPalette) private var palette
@Environment(\.colorScheme) private var colorScheme
private let banner: Banner
public init(banner: Banner) {
self.banner = banner
}
public var body: some View {
HStack(alignment: .top) {
if let bannerAction = banner.action {
Button(action: bannerAction.action, label: {
self.contentView
self.button(for: bannerAction)
})
} else {
self.contentView
}
}
.conditionallyApplyWidthConstraints()
.padding(.regular)
.padding(.horizontal, .regular)
.background(self.background)
.background(.ultraThinMaterial.standardDropShadow())
.containerShape(.rect(cornerRadius: .regular))
.borderOverlay(withShape: .rect(cornerRadius: .regular), border: palette.alternativeBackground, width: .hairline)
.padding(.horizontal, self.horizontalPadding)
}
} And the system is tied together by conforming extension Banner: MessageViewConvertible {
public func asMessageView() -> some View {
BannerView(banner: self)
.padding(.large)
.padding(.vertical, .huge + .large)
}
} Now whenever I want to present a banner, I pass in one of the pre-defined banners, like so. // Called from anywhere in my app
self.bannersController.present(.linkCopied)
// Predefined banners
public extension Banner {
static let linkCopied = Banner.info(
title: LocalizedStringKey("BANNER_LINK_COPIED_TITLE", bundle: .module),
image: Image.icons.link
)
} I believe I can port this over to the |
With non-modifier approach, my initial thought is to put something in the environment your banner can use to dismiss, similar to Apple's public struct BannerView: View {
@Environment(\.swiftmessagesHide) private var hide
private let banner: Banner
public init(banner: Banner) {
self.banner = banner
}
public var body: some View {
//...
Button("OK") {
hide()
}
}
} However, with this approach I think you'd need to also configure the environment value with a modifier in your root view: public struct RootView: View {
public var body: some View {
Whatever()
.installSwiftMessages()
}
} This will only allow you to dismiss the currently displayed banner. However, since presumably the user is tapping a button in the currently displayed banner, it seems sufficient. This is a pretty small change I could make in the next week or so. |
I thought of another option that seems preferable to me and would require any SwiftMessages changes. There could be a flaw in this thinking, but:
|
Make something like the
dismiss
environment value for SwiftMessages that will allow SwiftUI messages to self-dismiss.The text was updated successfully, but these errors were encountered: