Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7c89b23
build: require iOS 16
nighthawk Oct 8, 2026
be37cbc
feat: show cards in a system sheet on iOS 26+
nighthawk Oct 8, 2026
9496578
docs: describe system sheet presentation
nighthawk Oct 8, 2026
0727ed4
fix: keep present and dismiss callable from non-isolated code
nighthawk Oct 8, 2026
38bc728
perf: take covered cards out of the view hierarchy
nighthawk Oct 9, 2026
cc1329f
fix: clip cards to the sheet's rounded corners
nighthawk Oct 9, 2026
8827975
feat: cross-fade cards when pushing and popping in a system sheet
nighthawk Oct 9, 2026
2d89bb8
fix(example): adopt the scene lifecycle
nighthawk Oct 9, 2026
75f8c9a
feat: put the close button where the system does next to a vertical bar
nighthawk Oct 9, 2026
9c9eafb
feat: show card actions and paging in the vertical bar
nighthawk Oct 9, 2026
9b1b9fa
fix: keep cards in a sheet clear of a vertical bar
nighthawk Oct 9, 2026
ce5b6fa
fix: balance a collapsed card's title in a floating sheet
nighthawk Oct 9, 2026
d6a1986
feat: fade in a background colour behind raised cards
nighthawk Oct 9, 2026
33bda3d
fix: show a raised card's background across the whole sheet
nighthawk Oct 9, 2026
faf8067
fix: keep building with Xcode 26, which lacks the vertical bar APIs
nighthawk Oct 9, 2026
6f96ad0
ci: build the Example for any simulator
nighthawk Oct 9, 2026
d43384a
fix: keep building for visionOS and Mac Catalyst
nighthawk Oct 9, 2026
2184a3b
refactor: name bar actions after the vertical bar
nighthawk Oct 9, 2026
7ef0e5a
fix: only show the expanded background in system sheets
nighthawk Oct 9, 2026
cc99652
fix: keep cards clear of a vertical bar on the leading edge
nighthawk Oct 9, 2026
f42e2d1
feat: show cards in an adaptive system sheet on iOS 27+ only
nighthawk Oct 9, 2026
4182e8d
feat: optionally keep the sheet beside a vertical bar
nighthawk Oct 11, 2026
e8670a3
feat: keep the sheet beside a vertical bar by default
nighthawk Oct 11, 2026
ef4f35a
revert: let the sheet extend underneath a vertical bar again
nighthawk Oct 12, 2026
428a672
feat: use the full width for collapsed cards next to a vertical bar
nighthawk Oct 12, 2026
8949cf2
feat: let the bottom map buttons use the bar's column when collapsed
nighthawk Oct 12, 2026
652dd14
fix: apply the collapsed layout next to a vertical bar without bar items
nighthawk Oct 12, 2026
ef04d0d
fix: measure the sheet's height from its own bottom edge
nighthawk Oct 12, 2026
c263007
feat: let cards span the sheet under a vertical bar
nighthawk Oct 12, 2026
dec6266
fix: lay out the cards again after the vertical bar inset changes
nighthawk Oct 12, 2026
b21075a
feat: span the header across the top above a system sheet
nighthawk Oct 12, 2026
316fe00
feat: align the header with a system sheet on the side
nighthawk Oct 12, 2026
4659e85
feat: give the header the same background as the card in the sheet
nighthawk Oct 12, 2026
44df502
feat: round the floating header's top corners concentrically
nighthawk Oct 12, 2026
e4be4c7
fix: keep all pages clear of a vertical bar on the leading edge
nighthawk Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ jobs:
- name: Build Example
run: |
cd Example
set -o pipefail && xcodebuild build -scheme 'Example' -destination 'platform=iOS Simulator,name=iPhone 16' | xcbeautify
set -o pipefail && xcodebuild build -scheme 'Example' -destination 'generic/platform=iOS Simulator' | xcbeautify
8 changes: 8 additions & 0 deletions Example/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

}


/// Required on iOS 27, which no longer launches apps without scene support.
/// The window and its root view controller come from `Main.storyboard`.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

}
4 changes: 2 additions & 2 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -416,7 +416,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
Expand Down
6 changes: 6 additions & 0 deletions Example/ExampleCardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class ExampleCardViewController: TGCardViewController {

navigationButtonsAreSpringLoaded = true

// Pass `-classicCards` as a launch argument to compare with the classic
// presentation, where the cards aren't in a system sheet.
if ProcessInfo.processInfo.arguments.contains("-classicCards") {
presentationStyle = .classic
}

#if targetEnvironment(macCatalyst)
mode = .sidebar
#endif
Expand Down
19 changes: 19 additions & 0 deletions Example/Supporting Files/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@
<string>Find yourself on the map</string>
<key>NSPrincipalClass</key>
<string></string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
Expand Down
27 changes: 27 additions & 0 deletions Example/cards/ExampleCityCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Foundation
import MapKit
import UIKit

import TGCardViewController

Expand Down Expand Up @@ -42,6 +43,32 @@ class ExampleCityCard : TGPlainCard {
init(city: City) {
self.city = city
super.init(title: .default(city.title, nil, nil), mapManager: city.manager)
updateBarActions()
}

private var isFavorite = false

/// Only shown next to a vertical bar, e.g., on the iPhone Duo. A real card
/// would show them itself when `showsVerticalBarActions` is `false`.
private func updateBarActions() {
verticalBarActions = [
UIAction(
title: isFavorite ? "Remove Favourite" : "Add Favourite",
image: UIImage(systemName: isFavorite ? "star.fill" : "star")
) { [weak self] _ in
self?.isFavorite.toggle()
self?.updateBarActions()
},
UIAction(
title: "Share",
image: UIImage(systemName: "square.and.arrow.up")
) { [weak self] action in
guard let self else { return }
let activity = UIActivityViewController(activityItems: [city.title], applicationActivities: nil)
activity.popoverPresentationController?.sourceItem = action.sender as? UIPopoverPresentationControllerSourceItem
controller?.present(activity, animated: true)
},
]
}

}
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.3
// swift-tools-version:5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -7,7 +7,7 @@ let package = Package(
name: "TGCardViewController",
defaultLocalization: "en",
platforms: [
.iOS(.v13)
.iOS(.v16)
],
products: [
.library(
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,35 @@ Provides a card-based view controller for mapping applications where the card's
}
```

### System sheets on iOS 27+

By default (`presentationStyle = .automatic`), 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 |

Set `presentationStyle` to `.classic` to opt out. 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
Expand Down
Loading
Loading