Adds presentation background/content interactions and corner radius
This commit is contained in:
parent
e6d5b78c69
commit
00b13d90ea
|
@ -0,0 +1,163 @@
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftBackports
|
||||||
|
|
||||||
|
@available(iOS, deprecated: 16.4)
|
||||||
|
@available(tvOS, deprecated: 16.4)
|
||||||
|
@available(macOS, deprecated: 13.3)
|
||||||
|
@available(watchOS, deprecated: 9.4)
|
||||||
|
extension Backport<Any> {
|
||||||
|
/// The kinds of interaction available to views behind a presentation.
|
||||||
|
///
|
||||||
|
/// Use values of this type with the
|
||||||
|
/// ``View/presentationBackgroundInteraction(_:)`` modifier.
|
||||||
|
public struct PresentationBackgroundInteraction: Hashable {
|
||||||
|
enum Interaction: Hashable {
|
||||||
|
case automatic
|
||||||
|
case enabled
|
||||||
|
case upThrough(detent: Backport.PresentationDetent)
|
||||||
|
case disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
let interaction: Interaction
|
||||||
|
|
||||||
|
/// The default background interaction for the presentation.
|
||||||
|
public static var automatic: Self { .init(interaction: .automatic) }
|
||||||
|
|
||||||
|
/// People can interact with the view behind a presentation.
|
||||||
|
public static var enabled: Self { .init(interaction: .enabled) }
|
||||||
|
|
||||||
|
/// People can interact with the view behind a presentation up through a
|
||||||
|
/// specified detent.
|
||||||
|
///
|
||||||
|
/// At detents larger than the one you specify, SwiftUI disables
|
||||||
|
/// interaction.
|
||||||
|
///
|
||||||
|
/// - Parameter detent: The largest detent at which people can interact with
|
||||||
|
/// the view behind the presentation.
|
||||||
|
public static func enabled(upThrough detent: Backport.PresentationDetent) -> Self { .init(interaction: .upThrough(detent: detent))}
|
||||||
|
|
||||||
|
/// People can't interact with the view behind a presentation.
|
||||||
|
public static var disabled: Self { .init(interaction: .disabled) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS, deprecated: 16.4)
|
||||||
|
@available(tvOS, deprecated: 16.4)
|
||||||
|
@available(macOS, deprecated: 13.3)
|
||||||
|
@available(watchOS, deprecated: 9.4)
|
||||||
|
public extension Backport where Wrapped: View {
|
||||||
|
/// Controls whether people can interact with the view behind a
|
||||||
|
/// presentation.
|
||||||
|
///
|
||||||
|
/// On many platforms, SwiftUI automatically disables the view behind a
|
||||||
|
/// sheet that you present, so that people can't interact with the backing
|
||||||
|
/// view until they dismiss the sheet. Use this modifier if you want to
|
||||||
|
/// enable interaction.
|
||||||
|
///
|
||||||
|
/// The following example enables people to interact with the view behind
|
||||||
|
/// the sheet when the sheet is at the smallest detent, but not at the other
|
||||||
|
/// detents:
|
||||||
|
///
|
||||||
|
/// struct ContentView: View {
|
||||||
|
/// @State private var showSettings = false
|
||||||
|
///
|
||||||
|
/// var body: some View {
|
||||||
|
/// Button("View Settings") {
|
||||||
|
/// showSettings = true
|
||||||
|
/// }
|
||||||
|
/// .sheet(isPresented: $showSettings) {
|
||||||
|
/// SettingsView()
|
||||||
|
/// .presentationDetents(
|
||||||
|
/// [.medium, .large])
|
||||||
|
/// .presentationBackgroundInteraction(
|
||||||
|
/// .enabled(upThrough: .medium))
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - interaction: A specification of how people can interact with the
|
||||||
|
/// view behind a presentation.
|
||||||
|
@ViewBuilder
|
||||||
|
func presentationBackgroundInteraction(_ interaction: Backport<Any>.PresentationBackgroundInteraction) -> some View {
|
||||||
|
#if os(iOS)
|
||||||
|
if #available(iOS 15, *) {
|
||||||
|
wrapped.background(Backport<Any>.Representable(interaction: interaction))
|
||||||
|
} else {
|
||||||
|
wrapped
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
wrapped
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 15, *)
|
||||||
|
private extension Backport where Wrapped == Any {
|
||||||
|
struct Representable: UIViewControllerRepresentable {
|
||||||
|
let interaction: Backport<Any>.PresentationBackgroundInteraction
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> Backport.Representable.Controller {
|
||||||
|
Controller(interaction: interaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ controller: Backport.Representable.Controller, context: Context) {
|
||||||
|
controller.update(interaction: interaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 15, *)
|
||||||
|
private extension Backport.Representable {
|
||||||
|
final class Controller: UIViewController, UISheetPresentationControllerDelegate {
|
||||||
|
var interaction: Backport<Any>.PresentationBackgroundInteraction
|
||||||
|
weak var _delegate: UISheetPresentationControllerDelegate?
|
||||||
|
|
||||||
|
init(interaction: Backport<Any>.PresentationBackgroundInteraction) {
|
||||||
|
self.interaction = interaction
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willMove(toParent parent: UIViewController?) {
|
||||||
|
super.willMove(toParent: parent)
|
||||||
|
update(interaction: interaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
|
super.willTransition(to: newCollection, with: coordinator)
|
||||||
|
update(interaction: interaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(interaction: Backport<Any>.PresentationBackgroundInteraction) {
|
||||||
|
self.interaction = interaction
|
||||||
|
|
||||||
|
if let controller = parent?.sheetPresentationController {
|
||||||
|
controller.animateChanges {
|
||||||
|
switch interaction.interaction {
|
||||||
|
case .automatic:
|
||||||
|
controller.largestUndimmedDetentIdentifier = nil
|
||||||
|
controller.presentingViewController.view?.tintAdjustmentMode = .automatic
|
||||||
|
case .disabled:
|
||||||
|
controller.largestUndimmedDetentIdentifier = nil
|
||||||
|
controller.presentingViewController.view?.tintAdjustmentMode = .automatic
|
||||||
|
case .enabled:
|
||||||
|
controller.largestUndimmedDetentIdentifier = .large
|
||||||
|
controller.presentingViewController.view?.tintAdjustmentMode = .normal
|
||||||
|
case .upThrough(let detent):
|
||||||
|
controller.largestUndimmedDetentIdentifier = .init(detent.id.rawValue)
|
||||||
|
|
||||||
|
let selectedId = controller.selectedDetentIdentifier ?? .large
|
||||||
|
let selected = Backport<Any>.PresentationDetent(id: .init(rawValue: selectedId.rawValue))
|
||||||
|
controller.presentingViewController.view?.tintAdjustmentMode = selected > detent ? .dimmed : .normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,113 @@
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftBackports
|
||||||
|
|
||||||
|
@available(iOS, deprecated: 16.4)
|
||||||
|
@available(tvOS, deprecated: 16.4)
|
||||||
|
@available(macOS, deprecated: 13.3)
|
||||||
|
@available(watchOS, deprecated: 9.4)
|
||||||
|
public extension Backport<Any> {
|
||||||
|
/// A behavior that you can use to influence how a presentation responds to
|
||||||
|
/// swipe gestures.
|
||||||
|
///
|
||||||
|
/// Use values of this type with the
|
||||||
|
/// ``View/presentationContentInteraction(_:)`` modifier.
|
||||||
|
struct PresentationContentInteraction: Hashable {
|
||||||
|
enum Interaction: Hashable {
|
||||||
|
case automatic
|
||||||
|
case resizes
|
||||||
|
case scrolls
|
||||||
|
}
|
||||||
|
|
||||||
|
let interaction: Interaction
|
||||||
|
|
||||||
|
/// The default swipe behavior for the presentation.
|
||||||
|
public static var automatic: PresentationContentInteraction { .init(interaction: .automatic) }
|
||||||
|
|
||||||
|
/// A behavior that prioritizes resizing a presentation when swiping, rather
|
||||||
|
/// than scrolling the content of the presentation.
|
||||||
|
public static var resizes: PresentationContentInteraction { .init(interaction: .resizes) }
|
||||||
|
|
||||||
|
/// A behavior that prioritizes scrolling the content of a presentation when
|
||||||
|
/// swiping, rather than resizing the presentation.
|
||||||
|
public static var scrolls: PresentationContentInteraction { .init(interaction: .scrolls) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS, deprecated: 16.4)
|
||||||
|
@available(tvOS, deprecated: 16.4)
|
||||||
|
@available(macOS, deprecated: 13.3)
|
||||||
|
@available(watchOS, deprecated: 9.4)
|
||||||
|
public extension Backport where Wrapped: View {
|
||||||
|
@ViewBuilder
|
||||||
|
func presentationContentInteraction(_ interaction: Backport<Any>.PresentationContentInteraction) -> some View {
|
||||||
|
#if os(iOS)
|
||||||
|
if #available(iOS 15, *) {
|
||||||
|
wrapped.background(Backport<Any>.Representable(interaction: interaction))
|
||||||
|
} else {
|
||||||
|
wrapped
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
wrapped
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 15, *)
|
||||||
|
private extension Backport where Wrapped == Any {
|
||||||
|
struct Representable: UIViewControllerRepresentable {
|
||||||
|
let interaction: Backport<Any>.PresentationContentInteraction
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> Backport.Representable.Controller {
|
||||||
|
Controller(interaction: interaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ controller: Backport.Representable.Controller, context: Context) {
|
||||||
|
controller.update(interaction: interaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 15, *)
|
||||||
|
private extension Backport.Representable {
|
||||||
|
final class Controller: UIViewController, UISheetPresentationControllerDelegate {
|
||||||
|
var interaction: Backport<Any>.PresentationContentInteraction
|
||||||
|
|
||||||
|
init(interaction: Backport<Any>.PresentationContentInteraction) {
|
||||||
|
self.interaction = interaction
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willMove(toParent parent: UIViewController?) {
|
||||||
|
super.willMove(toParent: parent)
|
||||||
|
update(interaction: interaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
|
super.willTransition(to: newCollection, with: coordinator)
|
||||||
|
update(interaction: interaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(interaction: Backport<Any>.PresentationContentInteraction) {
|
||||||
|
self.interaction = interaction
|
||||||
|
|
||||||
|
if let controller = parent?.sheetPresentationController {
|
||||||
|
controller.animateChanges {
|
||||||
|
switch interaction.interaction {
|
||||||
|
case .automatic, .resizes:
|
||||||
|
controller.prefersScrollingExpandsWhenScrolledToEdge = true
|
||||||
|
case .scrolls:
|
||||||
|
controller.prefersScrollingExpandsWhenScrolledToEdge = false
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.preferredCornerRadius = 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,74 @@
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftBackports
|
||||||
|
|
||||||
|
@available(iOS, deprecated: 16.4)
|
||||||
|
@available(tvOS, deprecated: 16.4)
|
||||||
|
@available(macOS, deprecated: 13.3)
|
||||||
|
@available(watchOS, deprecated: 9.4)
|
||||||
|
public extension Backport where Wrapped: View {
|
||||||
|
@ViewBuilder
|
||||||
|
func presentationCornerRadius(_ cornerRadius: CGFloat?) -> some View {
|
||||||
|
#if os(iOS)
|
||||||
|
if #available(iOS 15, *) {
|
||||||
|
wrapped.background(Backport<Any>.Representable(cornerRadius: cornerRadius))
|
||||||
|
} else {
|
||||||
|
wrapped
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
wrapped
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 15, *)
|
||||||
|
private extension Backport where Wrapped == Any {
|
||||||
|
struct Representable: UIViewControllerRepresentable {
|
||||||
|
let cornerRadius: CGFloat?
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> Backport.Representable.Controller {
|
||||||
|
Controller(cornerRadius: cornerRadius)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ controller: Backport.Representable.Controller, context: Context) {
|
||||||
|
controller.update(cornerRadius: cornerRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 15, *)
|
||||||
|
private extension Backport.Representable {
|
||||||
|
final class Controller: UIViewController, UISheetPresentationControllerDelegate {
|
||||||
|
var cornerRadius: CGFloat?
|
||||||
|
|
||||||
|
init(cornerRadius: CGFloat?) {
|
||||||
|
self.cornerRadius = cornerRadius
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willMove(toParent parent: UIViewController?) {
|
||||||
|
super.willMove(toParent: parent)
|
||||||
|
update(cornerRadius: cornerRadius)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
|
super.willTransition(to: newCollection, with: coordinator)
|
||||||
|
update(cornerRadius: cornerRadius)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(cornerRadius: CGFloat?) {
|
||||||
|
self.cornerRadius = cornerRadius
|
||||||
|
|
||||||
|
if let controller = parent?.sheetPresentationController {
|
||||||
|
controller.animateChanges {
|
||||||
|
controller.preferredCornerRadius = cornerRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -5,7 +5,6 @@ import SwiftBackports
|
||||||
@available(macOS, deprecated: 13)
|
@available(macOS, deprecated: 13)
|
||||||
@available(watchOS, deprecated: 9)
|
@available(watchOS, deprecated: 9)
|
||||||
public extension Backport where Wrapped: View {
|
public extension Backport where Wrapped: View {
|
||||||
|
|
||||||
/// Sets the available detents for the enclosing sheet.
|
/// Sets the available detents for the enclosing sheet.
|
||||||
///
|
///
|
||||||
/// By default, sheets support the ``PresentationDetent/large`` detent.
|
/// By default, sheets support the ``PresentationDetent/large`` detent.
|
||||||
|
@ -31,39 +30,7 @@ public extension Backport where Wrapped: View {
|
||||||
@available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+")
|
@available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+")
|
||||||
func presentationDetents(_ detents: Set<Backport<Any>.PresentationDetent>) -> some View {
|
func presentationDetents(_ detents: Set<Backport<Any>.PresentationDetent>) -> some View {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
wrapped.background(Backport<Any>.Representable(detents: detents, selection: nil, largestUndimmed: .large))
|
wrapped.background(Backport<Any>.Representable(detents: detents, selection: nil))
|
||||||
#else
|
|
||||||
wrapped
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Sets the available detents for the enclosing sheet.
|
|
||||||
///
|
|
||||||
/// By default, sheets support the ``PresentationDetent/large`` detent.
|
|
||||||
///
|
|
||||||
/// struct ContentView: View {
|
|
||||||
/// @State private var showSettings = false
|
|
||||||
///
|
|
||||||
/// var body: some View {
|
|
||||||
/// Button("View Settings") {
|
|
||||||
/// showSettings = true
|
|
||||||
/// }
|
|
||||||
/// .sheet(isPresented: $showSettings) {
|
|
||||||
/// SettingsView()
|
|
||||||
/// .presentationDetents([.medium, .large])
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - Parameter detents: A set of supported detents for the sheet.
|
|
||||||
/// If you provide more than one detent, people can drag the sheet
|
|
||||||
/// to resize it.
|
|
||||||
@ViewBuilder
|
|
||||||
@available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+")
|
|
||||||
func presentationDetents(_ detents: Set<Backport<Any>.PresentationDetent>, largestUndimmedDetent: Backport<Any>.PresentationDetent? = nil) -> some View {
|
|
||||||
#if os(iOS)
|
|
||||||
wrapped.background(Backport<Any>.Representable(detents: detents, selection: nil, largestUndimmed: largestUndimmedDetent))
|
|
||||||
#else
|
#else
|
||||||
wrapped
|
wrapped
|
||||||
#endif
|
#endif
|
||||||
|
@ -104,59 +71,18 @@ public extension Backport where Wrapped: View {
|
||||||
@available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+")
|
@available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+")
|
||||||
func presentationDetents(_ detents: Set<Backport<Any>.PresentationDetent>, selection: Binding<Backport<Any>.PresentationDetent>) -> some View {
|
func presentationDetents(_ detents: Set<Backport<Any>.PresentationDetent>, selection: Binding<Backport<Any>.PresentationDetent>) -> some View {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
wrapped.background(Backport<Any>.Representable(detents: detents, selection: selection, largestUndimmed: .large))
|
wrapped.background(Backport<Any>.Representable(detents: detents, selection: selection))
|
||||||
#else
|
#else
|
||||||
wrapped
|
wrapped
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the available detents for the enclosing sheet, giving you
|
|
||||||
/// programmatic control of the currently selected detent.
|
|
||||||
///
|
|
||||||
/// By default, sheets support the ``PresentationDetent/large`` detent.
|
|
||||||
///
|
|
||||||
/// struct ContentView: View {
|
|
||||||
/// @State private var showSettings = false
|
|
||||||
/// @State private var settingsDetent = PresentationDetent.medium
|
|
||||||
///
|
|
||||||
/// var body: some View {
|
|
||||||
/// Button("View Settings") {
|
|
||||||
/// showSettings = true
|
|
||||||
/// }
|
|
||||||
/// .sheet(isPresented: $showSettings) {
|
|
||||||
/// SettingsView()
|
|
||||||
/// .presentationDetents:(
|
|
||||||
/// [.medium, .large],
|
|
||||||
/// selection: $settingsDetent
|
|
||||||
/// )
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - detents: A set of supported detents for the sheet.
|
|
||||||
/// If you provide more that one detent, people can drag the sheet
|
|
||||||
/// to resize it.
|
|
||||||
/// - selection: A ``Binding`` to the currently selected detent.
|
|
||||||
/// Ensure that the value matches one of the detents that you
|
|
||||||
/// provide for the `detents` parameter.
|
|
||||||
@ViewBuilder
|
|
||||||
@available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+")
|
|
||||||
func presentationDetents(_ detents: Set<Backport<Any>.PresentationDetent>, selection: Binding<Backport<Any>.PresentationDetent>, largestUndimmedDetent: Backport<Any>.PresentationDetent? = nil) -> some View {
|
|
||||||
#if os(iOS)
|
|
||||||
wrapped.background(Backport<Any>.Representable(detents: detents, selection: selection, largestUndimmed: largestUndimmedDetent))
|
|
||||||
#else
|
|
||||||
wrapped
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS, deprecated: 16)
|
@available(iOS, deprecated: 16)
|
||||||
@available(tvOS, deprecated: 16)
|
@available(tvOS, deprecated: 16)
|
||||||
@available(macOS, deprecated: 13)
|
@available(macOS, deprecated: 13)
|
||||||
@available(watchOS, deprecated: 9)
|
@available(watchOS, deprecated: 9)
|
||||||
public extension Backport where Wrapped == Any {
|
public extension Backport<Any> {
|
||||||
|
|
||||||
/// A type that represents a height where a sheet naturally rests.
|
/// A type that represents a height where a sheet naturally rests.
|
||||||
struct PresentationDetent: Hashable, Comparable {
|
struct PresentationDetent: Hashable, Comparable {
|
||||||
|
@ -206,18 +132,17 @@ public extension Backport where Wrapped == Any {
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@available(iOS 15, *)
|
@available(iOS 15, *)
|
||||||
private extension Backport where Wrapped == Any {
|
private extension Backport<Any> {
|
||||||
struct Representable: UIViewControllerRepresentable {
|
struct Representable: UIViewControllerRepresentable {
|
||||||
let detents: Set<Backport<Any>.PresentationDetent>
|
let detents: Set<Backport<Any>.PresentationDetent>
|
||||||
let selection: Binding<Backport<Any>.PresentationDetent>?
|
let selection: Binding<Backport<Any>.PresentationDetent>?
|
||||||
let largestUndimmed: Backport<Any>.PresentationDetent?
|
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> Backport.Representable.Controller {
|
func makeUIViewController(context: Context) -> Backport.Representable.Controller {
|
||||||
Controller(detents: detents, selection: selection, largestUndimmed: largestUndimmed)
|
Controller(detents: detents, selection: selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(_ controller: Backport.Representable.Controller, context: Context) {
|
func updateUIViewController(_ controller: Backport.Representable.Controller, context: Context) {
|
||||||
controller.update(detents: detents, selection: selection, largestUndimmed: largestUndimmed)
|
controller.update(detents: detents, selection: selection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,10 +156,9 @@ private extension Backport.Representable {
|
||||||
var largestUndimmed: Backport<Any>.PresentationDetent?
|
var largestUndimmed: Backport<Any>.PresentationDetent?
|
||||||
weak var _delegate: UISheetPresentationControllerDelegate?
|
weak var _delegate: UISheetPresentationControllerDelegate?
|
||||||
|
|
||||||
init(detents: Set<Backport<Any>.PresentationDetent>, selection: Binding<Backport<Any>.PresentationDetent>?, largestUndimmed: Backport<Any>.PresentationDetent?) {
|
init(detents: Set<Backport<Any>.PresentationDetent>, selection: Binding<Backport<Any>.PresentationDetent>?) {
|
||||||
self.detents = detents
|
self.detents = detents
|
||||||
self.selection = selection
|
self.selection = selection
|
||||||
self.largestUndimmed = largestUndimmed
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,18 +174,17 @@ private extension Backport.Representable {
|
||||||
controller.delegate = self
|
controller.delegate = self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update(detents: detents, selection: selection, largestUndimmed: largestUndimmed)
|
update(detents: detents, selection: selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
|
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
super.willTransition(to: newCollection, with: coordinator)
|
super.willTransition(to: newCollection, with: coordinator)
|
||||||
update(detents: detents, selection: selection, largestUndimmed: largestUndimmed)
|
update(detents: detents, selection: selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(detents: Set<Backport<Any>.PresentationDetent>, selection: Binding<Backport<Any>.PresentationDetent>?, largestUndimmed: Backport<Any>.PresentationDetent?) {
|
func update(detents: Set<Backport<Any>.PresentationDetent>, selection: Binding<Backport<Any>.PresentationDetent>?) {
|
||||||
self.detents = detents
|
self.detents = detents
|
||||||
self.selection = selection
|
self.selection = selection
|
||||||
self.largestUndimmed = largestUndimmed
|
|
||||||
|
|
||||||
if let controller = parent?.sheetPresentationController {
|
if let controller = parent?.sheetPresentationController {
|
||||||
controller.animateChanges {
|
controller.animateChanges {
|
||||||
|
@ -274,10 +197,6 @@ private extension Backport.Representable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.largestUndimmedDetentIdentifier = largestUndimmed.flatMap {
|
|
||||||
.init(rawValue: $0.id.rawValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let selection = selection {
|
if let selection = selection {
|
||||||
controller.selectedDetentIdentifier = .init(selection.wrappedValue.id.rawValue)
|
controller.selectedDetentIdentifier = .init(selection.wrappedValue.id.rawValue)
|
||||||
}
|
}
|
||||||
|
@ -286,8 +205,8 @@ private extension Backport.Representable {
|
||||||
}
|
}
|
||||||
|
|
||||||
UIView.animate(withDuration: 0.25) {
|
UIView.animate(withDuration: 0.25) {
|
||||||
if let undimmed = largestUndimmed {
|
if let undimmed = controller.largestUndimmedDetentIdentifier {
|
||||||
controller.presentingViewController.view?.tintAdjustmentMode = (selection?.wrappedValue ?? .large) >= undimmed ? .automatic : .normal
|
controller.presentingViewController.view?.tintAdjustmentMode = (selection?.wrappedValue ?? .large) >= .init(id: .init(rawValue: undimmed.rawValue)) ? .automatic : .normal
|
||||||
} else {
|
} else {
|
||||||
controller.presentingViewController.view?.tintAdjustmentMode = .automatic
|
controller.presentingViewController.view?.tintAdjustmentMode = .automatic
|
||||||
}
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftBackports
|
||||||
|
|
||||||
|
///// Strategies for adapting a presentation to a different size class.
|
||||||
|
/////
|
||||||
|
///// Use values of this type with the ``View/presentationCompactAdaptation(_:)``
|
||||||
|
///// and ``View/presentationCompactAdaptation(horizontal:vertical:)`` modifiers.
|
||||||
|
//@available(iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4, *)
|
||||||
|
//public struct PresentationAdaptation {
|
||||||
|
//
|
||||||
|
// /// Use the default presentation adaptation.
|
||||||
|
// public static var automatic: PresentationAdaptation { get }
|
||||||
|
//
|
||||||
|
// /// Don't adapt for the size class, if possible.
|
||||||
|
// public static var none: PresentationAdaptation { get }
|
||||||
|
//
|
||||||
|
// /// Prefer a popover appearance when adapting for size classes.
|
||||||
|
// public static var popover: PresentationAdaptation { get }
|
||||||
|
//
|
||||||
|
// /// Prefer a sheet appearance when adapting for size classes.
|
||||||
|
// public static var sheet: PresentationAdaptation { get }
|
||||||
|
//
|
||||||
|
// /// Prefer a full-screen-cover appearance when adapting for size classes.
|
||||||
|
// public static var fullScreenCover: PresentationAdaptation { get }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///// Specifies how to adapt a presentation to compact size classes.
|
||||||
|
/////
|
||||||
|
///// Some presentations adapt their appearance depending on the context. For
|
||||||
|
///// example, a sheet presentation over a vertically-compact view uses a
|
||||||
|
///// full-screen-cover appearance by default. Use this modifier to indicate
|
||||||
|
///// a custom adaptation preference. For example, the following code
|
||||||
|
///// uses a presentation adaptation value of ``PresentationAdaptation/none``
|
||||||
|
///// to request that the system not adapt the sheet in compact size classes:
|
||||||
|
/////
|
||||||
|
///// struct ContentView: View {
|
||||||
|
///// @State private var showSettings = false
|
||||||
|
/////
|
||||||
|
///// var body: some View {
|
||||||
|
///// Button("View Settings") {
|
||||||
|
///// showSettings = true
|
||||||
|
///// }
|
||||||
|
///// .sheet(isPresented: $showSettings) {
|
||||||
|
///// SettingsView()
|
||||||
|
///// .presentationDetents([.medium, .large])
|
||||||
|
///// .presentationCompactAdaptation(.none)
|
||||||
|
///// }
|
||||||
|
///// }
|
||||||
|
///// }
|
||||||
|
/////
|
||||||
|
///// If you want to specify different adaptations for each dimension,
|
||||||
|
///// use the ``View/presentationCompactAdaptation(horizontal:vertical:)``
|
||||||
|
///// method instead.
|
||||||
|
/////
|
||||||
|
///// - Parameter adaptation: The adaptation to use in either a horizontally
|
||||||
|
///// or vertically compact size class.
|
||||||
|
//public func presentationCompactAdaptation(_ adaptation: PresentationAdaptation) -> some View
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///// Specifies how to adapt a presentation to horizontally and vertically
|
||||||
|
///// compact size classes.
|
||||||
|
/////
|
||||||
|
///// Some presentations adapt their appearance depending on the context. For
|
||||||
|
///// example, a popover presentation over a horizontally-compact view uses a
|
||||||
|
///// sheet appearance by default. Use this modifier to indicate a custom
|
||||||
|
///// adaptation preference.
|
||||||
|
/////
|
||||||
|
///// struct ContentView: View {
|
||||||
|
///// @State private var showInfo = false
|
||||||
|
/////
|
||||||
|
///// var body: some View {
|
||||||
|
///// Button("View Info") {
|
||||||
|
///// showInfo = true
|
||||||
|
///// }
|
||||||
|
///// .popover(isPresented: $showInfo) {
|
||||||
|
///// InfoView()
|
||||||
|
///// .presentationCompactAdaptation(
|
||||||
|
///// horizontal: .popover,
|
||||||
|
///// vertical: .sheet)
|
||||||
|
///// }
|
||||||
|
///// }
|
||||||
|
///// }
|
||||||
|
/////
|
||||||
|
///// If you want to specify the same adaptation for both dimensions,
|
||||||
|
///// use the ``View/presentationCompactAdaptation(_:)`` method instead.
|
||||||
|
/////
|
||||||
|
///// - Parameters:
|
||||||
|
///// - horizontalAdaptation: The adaptation to use in a horizontally
|
||||||
|
///// compact size class.
|
||||||
|
///// - verticalAdaptation: The adaptation to use in a vertically compact
|
||||||
|
///// size class. In a size class that is both horizontally and vertically
|
||||||
|
///// compact, SwiftUI uses the `verticalAdaptation` value.
|
||||||
|
//public func presentationCompactAdaptation(horizontal horizontalAdaptation: PresentationAdaptation, vertical verticalAdaptation: PresentationAdaptation) -> some View
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///// Requests that the presentation have a specific corner radius.
|
||||||
|
/////
|
||||||
|
///// Use this modifier to change the corner radius of a presentation.
|
||||||
|
/////
|
||||||
|
///// struct ContentView: View {
|
||||||
|
///// @State private var showSettings = false
|
||||||
|
/////
|
||||||
|
///// var body: some View {
|
||||||
|
///// Button("View Settings") {
|
||||||
|
///// showSettings = true
|
||||||
|
///// }
|
||||||
|
///// .sheet(isPresented: $showSettings) {
|
||||||
|
///// SettingsView()
|
||||||
|
///// .presentationDetents([.medium, .large])
|
||||||
|
///// .presentationCornerRadius(21)
|
||||||
|
///// }
|
||||||
|
///// }
|
||||||
|
///// }
|
||||||
|
/////
|
||||||
|
///// - Parameter cornerRadius: The corner radius, or `nil` to use the system
|
||||||
|
///// default.
|
||||||
|
//public func presentationCornerRadius(_ cornerRadius: CGFloat?) -> some View
|
Loading…
Reference in New Issue