-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Brandon Rodriguez
authored and
Brandon Rodriguez
committed
Mar 28, 2024
1 parent
562c0a2
commit c318942
Showing
6 changed files
with
280 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// | ||
// PartialSheetDetent.swift | ||
// iosApp | ||
// | ||
// Created by Brandon Rodriguez on 3/27/24. | ||
// Copyright © 2024 MBTA. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
/// Replaces the use of PresentationDetent from the SwiftUI package as it's only available iOS 16+ | ||
public enum PartialSheetDetent: String, Comparable { | ||
@available(iOS 16, *) | ||
case small = "com.mbta.small" | ||
case medium = "com.apple.UIKit.medium" | ||
case large = "com.apple.UIKit.large" | ||
|
||
public static func < (lhs: Self, rhs: Self) -> Bool { | ||
lhs.ordinal < rhs.ordinal | ||
} | ||
|
||
public var uiKitDetent: UISheetPresentationController.Detent { | ||
switch self { | ||
case .small: | ||
if #available(iOS 16, *) { | ||
let smallDetentIdentifier = UISheetPresentationController.Detent.Identifier(Self.small.rawValue) | ||
return UISheetPresentationController.Detent.custom(identifier: smallDetentIdentifier) { _ in | ||
120 | ||
} | ||
} else { | ||
return .medium() | ||
} | ||
case .medium: | ||
return .medium() | ||
case .large: | ||
return .large() | ||
} | ||
} | ||
|
||
private var ordinal: Int { | ||
switch self { | ||
case .small: | ||
0 | ||
case .medium: | ||
1 | ||
case .large: | ||
2 | ||
} | ||
} | ||
} |
156 changes: 156 additions & 0 deletions
156
iosApp/iosApp/Utils/Backport/PartialSheetModifier.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// | ||
// PartialSheetModifier.swift | ||
// iosApp | ||
// | ||
// Created by Brandon Rodriguez on 3/25/24. | ||
// Copyright © 2024 MBTA. All rights reserved. | ||
// | ||
|
||
import SwiftUI | ||
|
||
public extension View { | ||
/** | ||
Replaces the use of the presentationDetents & presentationBackgroundInteraction modifiers | ||
as they're only available in iOS 16+ for SwiftUI | ||
**/ | ||
func partialSheetDetents( | ||
_ detents: Set<PartialSheetDetent>, | ||
currentDetent: Binding<PartialSheetDetent>, | ||
largestUndimmedDetent: PartialSheetDetent | ||
) -> some View { | ||
background( | ||
PartialSheetRepresentable( | ||
detents: detents, | ||
currentDetent: currentDetent, | ||
largestUndimmedDetent: largestUndimmedDetent | ||
) | ||
) | ||
} | ||
} | ||
|
||
private struct PartialSheetRepresentable: UIViewControllerRepresentable { | ||
let detents: Set<PartialSheetDetent> | ||
let currentDetent: Binding<PartialSheetDetent>? | ||
let largestUndimmedDetent: PartialSheetDetent? | ||
|
||
func makeUIViewController(context _: Context) -> Self.Controller { | ||
Controller( | ||
detents: detents, | ||
currentDetent: currentDetent, | ||
largestUndimmedDetent: largestUndimmedDetent | ||
) | ||
} | ||
|
||
func updateUIViewController(_ controller: Self.Controller, context _: Context) { | ||
controller.update( | ||
detents: detents, | ||
currentDetent: currentDetent, | ||
largestUndimmedDetent: largestUndimmedDetent | ||
) | ||
} | ||
} | ||
|
||
private extension PartialSheetRepresentable { | ||
final class Controller: UIViewController, UISheetPresentationControllerDelegate { | ||
var detents: Set<PartialSheetDetent> | ||
var currentDetent: Binding<PartialSheetDetent>? | ||
var largestUndimmedDetent: PartialSheetDetent? | ||
weak var localDelegate: UISheetPresentationControllerDelegate? | ||
|
||
init( | ||
detents: Set<PartialSheetDetent>, | ||
currentDetent: Binding<PartialSheetDetent>?, | ||
largestUndimmedDetent: PartialSheetDetent? | ||
) { | ||
self.detents = detents | ||
self.currentDetent = currentDetent | ||
self.largestUndimmedDetent = largestUndimmedDetent | ||
super.init(nibName: nil, bundle: nil) | ||
} | ||
|
||
@available(*, unavailable) | ||
required init?(coder _: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
override func willMove(toParent parent: UIViewController?) { | ||
super.willMove(toParent: parent) | ||
if let controller = parent?.sheetPresentationController, | ||
controller.delegate !== self, | ||
localDelegate == nil | ||
{ | ||
localDelegate = controller.delegate | ||
controller.delegate = self | ||
} | ||
update(detents: detents, currentDetent: currentDetent, largestUndimmedDetent: largestUndimmedDetent) | ||
} | ||
|
||
override func willTransition( | ||
to newCollection: UITraitCollection, | ||
with coordinator: UIViewControllerTransitionCoordinator | ||
) { | ||
super.willTransition(to: newCollection, with: coordinator) | ||
update(detents: detents, currentDetent: currentDetent, largestUndimmedDetent: largestUndimmedDetent) | ||
} | ||
|
||
override func responds(to aSelector: Selector!) -> Bool { | ||
if super.responds(to: aSelector) { return true } | ||
if localDelegate?.responds(to: aSelector) == true { return true } | ||
return false | ||
} | ||
|
||
override func forwardingTarget(for aSelector: Selector!) -> Any? { | ||
if super.responds(to: aSelector) { return self } | ||
return localDelegate | ||
} | ||
|
||
func update( | ||
detents: Set<PartialSheetDetent>, | ||
currentDetent: Binding<PartialSheetDetent>?, | ||
largestUndimmedDetent: PartialSheetDetent? | ||
) { | ||
self.detents = detents | ||
self.currentDetent = currentDetent | ||
|
||
guard let controller = parent?.sheetPresentationController else { return } | ||
|
||
var tintAdjustmentMode: UIView.TintAdjustmentMode { | ||
if let undimmedIdentifier = controller.largestUndimmedDetentIdentifier?.rawValue { | ||
let currentDetent = currentDetent?.wrappedValue ?? .large | ||
let undimmed = PartialSheetDetent(rawValue: undimmedIdentifier) ?? .large | ||
return currentDetent > undimmed ? .dimmed : .normal | ||
} else { | ||
return .automatic | ||
} | ||
} | ||
|
||
controller.animateChanges { | ||
controller.detents = detents.map(\.uiKitDetent) | ||
controller.prefersScrollingExpandsWhenScrolledToEdge = true | ||
|
||
if let currentDetent { | ||
controller.selectedDetentIdentifier = .init(currentDetent.wrappedValue.rawValue) | ||
} | ||
|
||
if let largestUndimmedDetent { | ||
controller.largestUndimmedDetentIdentifier = .init(largestUndimmedDetent.rawValue) | ||
} | ||
} | ||
|
||
UIView.animate(withDuration: 0.25) { | ||
controller.presentingViewController.view?.tintAdjustmentMode = tintAdjustmentMode | ||
} | ||
} | ||
|
||
func sheetPresentationControllerDidChangeSelectedDetentIdentifier( | ||
_ sheetPresentationController: UISheetPresentationController | ||
) { | ||
guard let currentDetent, | ||
let id = sheetPresentationController.selectedDetentIdentifier?.rawValue, | ||
currentDetent.wrappedValue.rawValue != id | ||
else { return } | ||
|
||
currentDetent.wrappedValue = .init(rawValue: id) ?? .large | ||
} | ||
} | ||
} |
Oops, something went wrong.