Skip to content

About

Card-based view controller for apps that display content cards with accompanying maps, similar to Apple Maps.

Topics

Resources

Security policy

Stars

10 stars

Watchers

7 watching

Forks

Repository files navigation

TGCardViewController

CI

Provides a card-based view controller for mapping applications where the card's content is in sync with a map, similar to how Apple Maps works. For an application of this see the TripKitUI SDK and TripGo by SkedGo.

Collapsed

Peaking

Expanded

Custom header


Installation and usage

Install

Via Swift Package Manager (recommended)
  1. Add it to your Package.swift file (or add it as a dependency through Xcode):
.package(url: "https://github.com/skedgo/TGCardViewController.git", from: "1.7.5")
Via CocoaPods
  1. Check out the repo and make it accessible to your project, e.g., as a git submodule

  2. Add it to your Podfile, e.g.:

    pod 'TGCardViewController

  3. Run pod update

Add it to your app

  1. Create a TGCardViewController subclass and use it in your storyboard

  2. Override init(coder:) so that the instance from the storyboard isn't used, but instead TGCardViewController.xib:

    import TGCardViewController
    
    class CardViewController: TGCardViewController {
    
      required init(coder aDecoder: NSCoder) {
        // When loading from the storyboard we don't want to use the controller
        // as defined in the storyboard but instead use the TGCardViewController.xib
        super.init(nibName: "TGCardViewController", bundle: TGCardViewController.bundle)
      }
    
      ...
    }
  3. Create a TGCard subclass, that represents the card at the top level, and add then push that in your view controller's viewDidLoad:

      override func viewDidLoad() {
        rootCard = MyRootCard()
        super.viewDidLoad()
      }

System sheets on iOS 27+

On iOS 27 and later, the cards are shown in a system sheet (UISheetPresentationController). The sheet adapts to the size classes by itself, like Maps':

Presentation
Compact width, e.g., iPhone in portrait Sheet at the bottom, full width
Otherwise, e.g., iPad or iPhone in landscape Sheet on the leading edge, 400pt wide
Mac Catalyst, visionOS, mode = .sidebar Classic
The card controller is itself presented, but not full screen Classic
iOS 26 and earlier, or built with Xcode 26 and earlier Classic

This can't be turned off: presentationStyle and its .classic option are deprecated on iOS 27, and will be removed once that's the minimum. Check usesSystemSheet for what's currently in use.

Cards, map managers, headers, map buttons and paging work the same in both. Differences with a system sheet:

  • The map isn't dimmed and stays interactive when the card is extended.
  • The sheet provides the grab handle, the material, the corners and the dragging.
  • When a header is showing, the extended card stops below it.
  • Sheets presented by cards (e.g., with detents) stack on top of the card's sheet.

Because the sheet is the card controller's presentedViewController:

  • present(_:animated:completion:) and dismiss(animated:completion:) on the card controller present on top of the sheet, and dismiss what's on top of it, but never the sheet itself.
  • Use presentedOverlayViewController to check what's presented on top of the cards, instead of presentedViewController.
  • Use cardOverlayView instead of view for views that you constrain to views inside a card, e.g., tooltips.

On devices with a vertical bar, such as the iPhone Duo's outer display (iOS 27.1+), the system puts a sheet's bar items into that bar. The card controller does the same for cards, like Mail does: the top card's close button moves to the top of the bar, followed by buttons for the previous and next page of a TGPageCard, and the card's verticalBarActions go to the bottom of the bar as icon-only buttons. They stay in the bar at every height of the sheet, and the cards stay clear of it. Cards can observe showsVerticalBarActions to leave those actions out of their own content while they're in the bar.

Specs

1. Basic functionality of cards

Behaviour:

  • Card positions:
    1. collapsed: Only shows header of card
    2. peaking: Shows half of card's content and the map content
    3. extended: Shows card fully, map shows a little bit on top but is greyed out
  • Pushing a card from another card
    • Adds (x) button unless it’s the root card
    • Card has a preferred position which is used when pushing
    • Animation: Slide up from the bottom; fading black view on card below with alpha from 0% to 25%
    • Pass on appearance callbacks appropriately to involved cards
  • Popping a card
    • Tap (x) to pop card
    • When popping top card, restore card position of card below when something got pushed on it
    • Animation: Slide back down; fading out black view on card below with alpha from 25% to 0%
    • Pass on appearance callbacks appropriately to involved cards
  • Cards are draggable
    • Snap to collapsed (only title), peaking (near half-way showing both map and card content), extended (still shows a bit of the map on top, but darkened)
    • Cards can be dragged up and down anywhere on the card
    • Tap title when collapsed: go to peaking
    • Tap title when peaking: go to extended
    • Tap title when extended: do nothing
    • Tap map when extended: go to peaking
  • Cards are scrollable
    • Cards typically have scrolling content: when scrolling down the card's header stays at the top and a bit of the map still keeps peaking through at the top.
    • When scrolling down show a thin separator line between the card's scrolling content and the card's header
    • When scrolling to the top and keeping to scroll, start dragging card

Styles:

  • Animation curve for push and pop
  • Blurry view under status bar (like Maps app)
  • When rotating device and card is collapsed, make sure card ends up in correct position

2. Card content and gestures

Card types:

  • Plain card
    • On top: Title, (x), optional subtitle and optional accessory view
    • Add accessory view
    • Content can be scrollable and size adjusts to content. If it fits, it shouldn’t be scrollable
    • Add optional floaty button
  • Table card
    • Same as plain card, but with a table view as its content
    • Allow specifying plain (e.g., for departures) or grouped style (e.g., for profile)
  • Collection card
    • Same as plain card, but with a collection view as its content
    • Allow specifying collection view layout
  • Hosting card
    • Allow using a SwiftUI view as the card's content
  • Paging card
    • Handles list of child cards on the same hierarchical level which can be paged programatically and through gestures
    • Has header view: Used for titles (child cards shouldn't show them then) and navigation; Header view is separate from sticky bar, i.e., you can have both.
    • Re-uses the top card's map manager
    • Pass on appearance callbacks appropriately to child cards

Card styles:

  • Rounded corners to cards
  • Grab handle for cards
  • Nice close buttons (and next button for paging cards)
  • Title and subtitle styling
  • Add mini drop shadow to card views
  • Bottom view

3. Map content

Map content:

  • Cards can optionally have map content
  • When showing the content, the insets should be respected to account for the card overlapping the map
  • If there’s no map content: Show card always extended and don't allow dragging it down (or just snap back up when using tries)

Map buttons:

  • Optional list of buttons that float on the right above the card or in the top right corner (when collapsed or peaking)
  • When dragging up the card to extended, the buttons fade away

4. Large width (iPad + iPhone in landscape)

  • Move card to the side with min (iPhone Plus) and max (iPad) width
  • Make sure transitions work when changing size and traits

5. UIKit features

  • State restoration, using NSUserActivity
  • VoiceOver Accessibility
  • Keyboard shortcuts

End-user documentation

Keyboard shortcuts

  • Card controller
    • ⌃+↑: Expand card
    • ⌃+↓: Expand/collapse card
    • ⌘+w: Pop card or dismiss modal
  • Table view cards
    • ↑: Highlight previous item
    • ↓: Highlight next item
    • ⌘+↑: Highlight item at start of list
    • ⌘+↓: Highlight item at end of list
    • Space or enter: select item
    • Esc: deselect
  • Paging cards
    • ⌃+←: Previous card
    • ⌃+→: Next card

About

Card-based view controller for apps that display content cards with accompanying maps, similar to Apple Maps.

Topics

Resources

Security policy

Stars

10 stars

Watchers

7 watching

Forks

Releases

Used by

Contributors

Languages