TTGSnackbar

June 13, 2026 · View on GitHub

简体中文

TTGSnackbar is a Swift implementation of an Android-style Snackbar for iOS. It presents short, actionable messages at the top or bottom of the screen, supports custom views, queue management, semantic styles, accessibility, haptics, and Swift concurrency.

Current development targets Swift 5.9, Xcode 15+, and iOS 16.0+.

Build Status Version License Platform Swift 5.9 Apps Using Total Download

Visual Overview

TTGSnackbar promotional poster

TTGSnackbar architecture overview

Features

  • Present messages at the bottom or top of the screen.
  • Add one or two action buttons, text actions, or icon-only actions.
  • Show built-in semantic styles: default, info, success, warning, error, and loading.
  • Present custom UIView content or display inside a custom container view.
  • Queue, replace, or deduplicate snackbars with TTGSnackbarManager.
  • Await action, tap, swipe, dismiss, drop, or presentation-failure results with Swift concurrency.
  • Support Dynamic Type, VoiceOver announcements, Reduce Motion, and haptic feedback.
  • Pause and resume dismiss timers manually, on touch, or during app lifecycle interruptions.
  • Include a privacy manifest for modern Apple platform requirements.

Requirements

TTGSnackbar versionSwiftXcodeiOS
Current5.915+16.0+
1.6.04.x9+8.0+
1.5.33.x8+8.0+

Installation

Swift Package Manager

Add this repository URL in Xcode:

https://github.com/zekunyan/TTGSnackbar.git

Then import the module:

import TTGSnackbar

CocoaPods

Add TTGSnackbar to your Podfile:

pod "TTGSnackbar"

Then run:

pod install

Carthage

Add TTGSnackbar to your Cartfile:

github "zekunyan/TTGSnackbar"

Quick Start

All snippets assume the module is already imported:

import TTGSnackbar

The default style works out of the box. Create a snackbar, configure only the behavior you need, then call show().

1. Show a simple message

Use this for short confirmation or status text that can disappear automatically.

let snackbar = TTGSnackbar(message: "TTGSnackbar!", duration: .short)
snackbar.show()

Simple snackbar

show() returns false if the snackbar cannot be presented, for example when no active window or custom container is available:

if !snackbar.show() {
    print("Snackbar could not be presented")
}

2. Add an action button

Use an action when the message should offer an immediate recovery path. Set both the visible title and the callback.

let snackbar = TTGSnackbar(
    message: "File deleted",
    duration: .middle,
    actionText: "Undo"
) { snackbar in
    restoreFile()
    snackbar.dismiss()
}

snackbar.show()

Action snackbar

3. Keep a snackbar visible while work runs

Use .forever for work that should stay visible until the user cancels or your task finishes. The .loading style adds the activity indicator.

let snackbar = TTGSnackbar(
    message: "Uploading…",
    duration: .forever
)

snackbar.style = .loading
snackbar.actionText = "Cancel"
snackbar.actionBlock = { snackbar in
    cancelUpload()
    snackbar.dismiss()
}

snackbar.show()

Long-running action

4. Use semantic feedback

Use built-in styles instead of hand-picking colors for common product states.

let snackbar = TTGSnackbar(message: "Saved successfully", duration: .middle)
snackbar.style = .success
snackbar.show()

Semantic snackbar

Available styles are .default, .info, .success, .warning, .error, and .loading.

5. Add a leading icon

Add an SF Symbol when the message benefits from a compact visual cue.

let snackbar = TTGSnackbar(message: "New feature ready", duration: .middle)
snackbar.icon = UIImage(systemName: "sparkles")
snackbar.show()

Icon snackbar

You can also use an icon-only action:

let snackbar = TTGSnackbar(message: "Tap the icon action", duration: .middle)
snackbar.actionIcon = UIImage(systemName: "hand.tap.fill")
snackbar.actionBlock = { snackbar in
    snackbar.dismiss()
}
snackbar.show()

6. Present custom content

Use a custom UIView when a single message label is not enough. The snackbar still manages presentation, margins, safe areas, and dismissal.

let contentView = UIStackView()
contentView.axis = .vertical
contentView.spacing = 4
contentView.isLayoutMarginsRelativeArrangement = true
contentView.layoutMargins = UIEdgeInsets(top: 16, left: 18, bottom: 16, right: 18)

let titleLabel = UILabel()
titleLabel.text = "Custom content view"
contentView.addArrangedSubview(titleLabel)

let snackbar = TTGSnackbar(customContentView: contentView, duration: .forever)
snackbar.shouldActivateLeftAndRightMarginOnCustomContentView = true
snackbar.show()

Custom content

Modern APIs

Semantic styles

Use semantic styles for common product states. Styles apply recommended colors, icons, loading behavior, and haptic defaults.

let success = TTGSnackbar(message: "Saved successfully", duration: .short)
success.style = .success
success.show()

let loading = TTGSnackbar(message: "Syncing…", duration: .forever)
loading.style = .loading
loading.show()

Available styles:

  • .default
  • .info
  • .success
  • .warning
  • .error
  • .loading

Configuration API

Use TTGSnackbarConfiguration to build snackbars from one value object. The property-based API remains supported.

let snackbar = TTGSnackbar(configuration: .init(
    message: "File deleted",
    duration: .long,
    style: .warning,
    actionText: "Undo",
    actionBlock: { snackbar in
        undoDelete()
        snackbar.dismiss()
    }
))

snackbar.show()

Async / await presentation

Swift concurrency callers can await the first action, tap, swipe, dismiss, manager drop, or presentation failure.

let result = await TTGSnackbar.show(configuration: .init(
    message: "File deleted",
    duration: .long,
    style: .warning,
    actionText: "Undo"
))

switch result {
case .action:
    undoDelete()
case .dismissed:
    break
case .dropped:
    print("A manager policy dropped the snackbar")
case .failedToPresent:
    print("No window or container was available")
default:
    break
}

The async helper wires action titles into result callbacks automatically. With property-based presentation, provide an actionBlock or secondActionBlock for action buttons to become visible.

Queue management

TTGSnackbarManager ensures only one managed snackbar is visible at a time.

let snackbar = TTGSnackbar(message: "Queued message", duration: .middle)
TTGSnackbarManager.shared.show(snackbar: snackbar, policy: .enqueue)

let urgent = TTGSnackbar(message: "Network disconnected", duration: .long)
urgent.style = .error
TTGSnackbarManager.shared.show(snackbar: urgent, policy: .replaceCurrent)

Available policies:

PolicyBehavior
.enqueueShow after the current snackbar is dismissed.
.replaceCurrentDismiss the current snackbar and show this one next.
.dropIfShowingSameMessageDrop when the same message is already visible or queued.

show(snackbar:policy:) returns false when the snackbar is synchronously dropped or cannot be presented.

let accepted = TTGSnackbarManager.shared.show(
    snackbar: snackbar,
    policy: .dropIfShowingSameMessage
)

Objective-C callers can use the same manager:

TTGSnackbar *bar = [[TTGSnackbar alloc] initWithMessage:@"Bar" duration:TTGSnackbarDurationMiddle];
[[TTGSnackbarManager shared] showSnackbar:bar policy:TTGSnackbarPresentationPolicyEnqueue];

Customization

Text and actions

PropertyDescription
messageMain message text. Supports multiline text and runtime updates.
messageTextColorMessage text color.
messageTextFontMessage text font.
messageTextAlignMessage text alignment.
messageContentInsetInsets for the message label text.
actionTextPrimary action title.
actionIconPrimary action icon.
actionTextColorPrimary action title color.
actionTextFontPrimary action font.
actionBlockPrimary action callback.
secondActionTextSecondary action title.
secondActionIconSecondary action icon.
secondActionTextColorSecondary action title color.
secondActionTextFontSecondary action font.
secondActionBlockSecondary action callback.
actionMaxWidthMaximum action-button width. Minimum value is 44.
actionTextNumberOfLinesNumber of action-button title lines.

Duration

duration defines how long the snackbar remains visible.

DurationBehavior
.short1 second.
.middle3 seconds.
.long5 seconds.
.customUses customDuration. Set customDuration > 0. Invalid values assert in debug builds and fall back to .short.
.foreverDoes not auto-dismiss. Dismiss manually.

Layout

PropertyDescription
animationTypeShow and dismiss animation style.
animationDurationShow and dismiss animation duration.
animationSpringWithDampingSpring damping used by animations.
animationInitialSpringVelocityInitial spring velocity used by animations.
leftMargin, rightMargin, topMargin, bottomMarginSnackbar margins.
contentInsetInsets around the built-in or custom content view.
cornerRadiusSnackbar corner radius.
snackbarMaxWidthMaximum snackbar width. Default is -1, which means full width.
containerViewCustom superview used for presentation instead of the active window.
customContentViewCustom content shown inside the snackbar.
shouldActivateLeftAndRightMarginOnCustomContentViewMakes custom content honor side margins.
shouldHonorSafeAreaLayoutGuidesUses safe area layout guides when positioning the snackbar.

Available animation types:

  • .fadeInFadeOut
  • .slideFromBottomToTop
  • .slideFromBottomBackToBottom
  • .slideFromLeftToRight
  • .slideFromRightToLeft
  • .slideFromTopToBottom
  • .slideFromTopBackToTop

Icon and indicator

PropertyDescription
iconLeading icon image.
iconContentModeLeading icon content mode.
iconBackgroundColorLeading icon background color.
iconTintColorLeading icon tint color.
iconImageViewWidthLeading icon width.
activityIndicatorViewStyleLoading indicator style.
activityIndicatorViewColorLoading indicator color.

Gestures and dismissal

Property / MethodDescription
onTapBlockCalled when the snackbar is tapped.
onSwipeBlockCalled when the snackbar is swiped.
shouldDismissOnSwipeAutomatically dismisses on swipe when enabled.
dismiss()Dismisses the snackbar with animation.
pauseDismissTimer()Pauses the auto-dismiss timer.
resumeDismissTimer()Resumes a paused auto-dismiss timer.
pausesDismissTimerOnTouchPauses timed snackbars while users touch them.
pausesDismissTimerWhenAppInactivePauses timed snackbars while the app is inactive.

Lifecycle callbacks

CallbackDescription
willShowBlockCalled before presentation animation starts.
didShowBlockCalled after presentation animation completes.
willDismissBlockCalled before dismissal animation starts.
didDismissBlockCalled after the snackbar is removed from its superview.
dismissBlockLegacy dismiss callback kept for compatibility.

Accessibility, motion, and haptics

PropertyDescription
shouldAnnounceForAccessibilityPosts a VoiceOver announcement when shown.
accessibilityAnnouncementCustom VoiceOver announcement. Defaults to message.
adjustsFontForContentSizeCategoryEnables Dynamic Type scaling for built-in labels/buttons.
shouldRespectReduceMotionUses a reduced fade animation when Reduce Motion is enabled.
hapticFeedbackFeedback played when the snackbar appears.
actionHapticFeedbackFeedback played when action buttons are tapped.

Examples

The repository includes Swift and Objective-C example apps that demonstrate the full feature gallery, including semantic styles, custom content, custom containers, manager policies, timer controls, accessibility, haptics, and async result handling.

License

TTGSnackbar is released under the MIT license. See LICENSE for details.