diff --git a/Example/ExampleCardViewController.swift b/Example/ExampleCardViewController.swift index 6711810..fd19db6 100644 --- a/Example/ExampleCardViewController.swift +++ b/Example/ExampleCardViewController.swift @@ -28,12 +28,6 @@ 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 diff --git a/README.md b/README.md index afd85c9..e9f2c4e 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ 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': +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 | |---|---| @@ -84,7 +84,7 @@ By default (`presentationStyle = .automatic`), on iOS 27 and later the cards are | 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. +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: diff --git a/Sources/TGCardViewController/TGCardViewController+SystemSheet.swift b/Sources/TGCardViewController/TGCardViewController+SystemSheet.swift index 36917d5..8d24c43 100644 --- a/Sources/TGCardViewController/TGCardViewController+SystemSheet.swift +++ b/Sources/TGCardViewController/TGCardViewController+SystemSheet.swift @@ -27,6 +27,10 @@ extension TGCardViewController { /// The card controller positions the cards itself, and they're dragged using /// its own gestures. + /// + /// Has no effect on iOS 27 and later, where cards always show in a system + /// sheet. On earlier versions, ``automatic`` does the same. + @available(iOS, deprecated: 27.0, message: "Cards always show in a system sheet on iOS 27 and later; earlier versions use the classic presentation anyway.") case classic } @@ -72,7 +76,6 @@ extension TGCardViewController { #if compiler(>=6.4) && os(iOS) && !targetEnvironment(macCatalyst) // Xcode 27 proxy, for the sheet placement API guard #available(iOS 27.0, *), - presentationStyle == .automatic, mode == .floating else { return false } @@ -124,17 +127,9 @@ extension TGCardViewController { installSystemSheet() } else if !wantsSheet, sheetHost != nil { uninstallSystemSheet(animated: topCardView == nil && isVisible) - } else if !wantsSheet, isAwaitingSystemSheet { - revealClassicCards() } } - private func revealClassicCards() { - guard isAwaitingSystemSheet else { return } - isAwaitingSystemSheet = false - toggleCardWrappers(hide: topCardView == nil) - } - } // MARK: - Installing and removing the sheet @@ -142,17 +137,28 @@ extension TGCardViewController { extension TGCardViewController { private func installSystemSheet() { + // Can't present while a transition is under way, e.g., when this got + // pushed onto a navigation stack, as `viewDidAppear` gets called before + // that transition fully completes. Keep the cards hidden and try again + // right after it, rather than showing them the classic way. + if let coordinator = transitionCoordinator { + coordinator.animate(alongsideTransition: nil) { [weak self] _ in + DispatchQueue.main.async { + self?.updateSystemSheetPresentation() + } + } + return + } + guard isVisible, view.window != nil, presentedViewController == nil, - transitionCoordinator == nil, let content = cardWrapperContent else { // We'll try again later, e.g., after the presented view controller got - // dismissed. Until then, show the cards in the classic way, as we don't - // know when that'll be. - revealClassicCards() + // dismissed. Until then, the cards stay hidden, as they never show the + // classic way when we want a sheet. return } @@ -191,7 +197,6 @@ extension TGCardViewController { // 2. Configure the sheet sheetHost = host - isAwaitingSystemSheet = false #if !os(visionOS) // No detents on visionOS, where cards never use a sheet if let sheet = host.sheetPresentationController { diff --git a/Sources/TGCardViewController/TGCardViewController.swift b/Sources/TGCardViewController/TGCardViewController.swift index a1bf30e..5d947a1 100644 --- a/Sources/TGCardViewController/TGCardViewController.swift +++ b/Sources/TGCardViewController/TGCardViewController.swift @@ -155,7 +155,9 @@ open class TGCardViewController: UIViewController { /// uses system sheets on iOS 27+. Check ``usesSystemSheet`` for what's /// currently used. /// - /// - Warning: Set before `viewDidLoad` is called. + /// - Note: Has no effect: cards always show in a system sheet on iOS 27 and + /// later, and the classic way before. + @available(iOS, deprecated: 27.0, message: "Cards always show in a system sheet on iOS 27 and later; earlier versions use the classic presentation anyway.") public var presentationStyle: PresentationStyle = .automatic // MARK: System sheet state, see `TGCardViewController+SystemSheet.swift` @@ -183,10 +185,6 @@ open class TGCardViewController: UIViewController { var sheetTargetPosition: TGCardPosition? var isSheetDraggingEnabled = true - /// Set while the initial cards wait for the sheet, so that they don't flash up - /// in the classic presentation first - var isAwaitingSystemSheet = false - /// Set while pushing or popping, which update the sheet in one go var isTransitioningCards = false @@ -728,9 +726,7 @@ open class TGCardViewController: UIViewController { didAddInitialCards = true // These get pushed the classic way and move into the sheet once we've - // appeared. Keep them hidden until then. - isAwaitingSystemSheet = wantsSystemSheet - + // appeared. They're hidden until then, see `toggleCardWrappers`. initialCards.forEach { push($0, animated: false, allowToNotify: $0 == initialCards.last, completionHandler: nil) } } @@ -900,8 +896,9 @@ open class TGCardViewController: UIViewController { } else { sidebarBackground.alpha = 0 } - // With a system sheet, the card wrapper is just an invisible placeholder - cardWrapperShadow.alpha = hide || usesSystemSheet || isAwaitingSystemSheet ? 0 : 1 + // With a system sheet, the card wrapper is just an invisible placeholder, + // and it stays invisible while waiting for the sheet to be presented + cardWrapperShadow.alpha = hide || usesSystemSheet || wantsSystemSheet ? 0 : 1 if !prepareOnly { if mode == .sidebar {