1
1
import SwiftUI
2
2
import SwiftBackports
3
+ import SwiftUIBackportsC
3
4
4
5
@available ( tvOS, deprecated: 16 )
5
6
@available ( macOS, deprecated: 13 )
@@ -30,7 +31,7 @@ public extension Backport where Wrapped: View {
30
31
@available ( iOS, introduced: 15 , deprecated: 16 , message: " Presentation detents are only supported in iOS 15+ " )
31
32
func presentationDetents( _ detents: Set < Backport < Any > . PresentationDetent > ) -> some View {
32
33
#if os(iOS)
33
- wrapped. background ( Backport< Any> . Representable( detents: detents, selection: nil ) )
34
+ wrapped. background ( Backport< Any> . Representable( detents: detents, largestUndimmed : nil , selection: nil ) )
34
35
#else
35
36
wrapped
36
37
#endif
@@ -69,9 +70,9 @@ public extension Backport where Wrapped: View {
69
70
/// provide for the `detents` parameter.
70
71
@ViewBuilder
71
72
@available ( iOS, introduced: 15 , deprecated: 16 , message: " Presentation detents are only supported in iOS 15+ " )
72
- func presentationDetents( _ detents: Set < Backport < Any > . PresentationDetent > , selection: Binding < Backport < Any > . PresentationDetent > ) -> some View {
73
+ func presentationDetents( _ detents: Set < Backport < Any > . PresentationDetent > , largestUndimmed : Backport < Any > . PresentationDetent ? = nil , selection: Binding < Backport < Any > . PresentationDetent > ) -> some View {
73
74
#if os(iOS)
74
- wrapped. background ( Backport< Any> . Representable( detents: detents, selection: selection) )
75
+ wrapped. background ( Backport< Any> . Representable( detents: detents, largestUndimmed : largestUndimmed , selection: selection) )
75
76
#else
76
77
wrapped
77
78
#endif
@@ -85,47 +86,70 @@ public extension Backport where Wrapped: View {
85
86
public extension Backport < Any > {
86
87
87
88
/// A type that represents a height where a sheet naturally rests.
88
- struct PresentationDetent : Hashable , Comparable {
89
+ enum PresentationDetent : Hashable , Comparable {
89
90
90
- public struct Identifier : RawRepresentable , Hashable {
91
- public var rawValue : String
92
- public init ( rawValue: String ) {
93
- self . rawValue = rawValue
94
- }
91
+ case medium, large, height( _ constant: CGFloat )
95
92
96
- public static var medium : Identifier {
97
- . init( rawValue: " com.apple.UIKit.medium " )
98
- }
93
+ @available ( iOS 15 . 0 , * )
94
+ init ? ( id: UISheetPresentationController . Detent . Identifier ) {
95
+ switch id {
96
+ case . medium:
97
+ self = . medium
98
+
99
+ case . large:
100
+ self = . large
99
101
100
- public static var large : Identifier {
101
- . init( rawValue: " com.apple.UIKit.large " )
102
+ default :
103
+ if let number = NumberFormatter ( ) . number ( from: id. rawValue) {
104
+ let value = CGFloat ( truncating: number)
105
+ self = . height( value)
106
+ } else {
107
+ return nil
108
+ }
109
+ }
102
110
}
103
- }
104
111
105
- public let id : Identifier
112
+ @available ( iOS 15 . 0 , * )
113
+ public var id : UISheetPresentationController . Detent . Identifier {
114
+ switch self {
115
+ case . medium:
116
+ return . medium
106
117
107
- /// The system detent for a sheet that's approximately half the height of
108
- /// the screen, and is inactive in compact height.
109
- public static var medium : PresentationDetent {
110
- . init( id: . medium)
111
- }
118
+ case . large:
119
+ return . large
112
120
113
- /// The system detent for a sheet at full height.
114
- public static var large : PresentationDetent {
115
- . init ( id : . large )
121
+ case let . height ( value ) :
122
+ return . init ( value . description )
123
+ }
116
124
}
117
125
118
- fileprivate static var none : PresentationDetent {
119
- return . init( id: . init( rawValue: " " ) )
126
+ @available ( iOS 15 . 0 , * )
127
+ var system : UISheetPresentationController . Detent {
128
+ switch self {
129
+ case . medium:
130
+ return . medium( )
131
+
132
+ case . large:
133
+ return . large( )
134
+
135
+ case let . height( constant) :
136
+ if #available( iOS 16 . 0 , * ) {
137
+ return . custom( identifier: id, resolver: { _ in constant} )
138
+ } else {
139
+ return . _detent( withIdentifier: id. rawValue, constant: constant)
140
+ }
141
+ }
120
142
}
121
143
122
144
public static func < ( lhs: PresentationDetent , rhs: PresentationDetent ) -> Bool {
123
- switch ( lhs, rhs) {
124
- case ( . large, . medium) :
125
- return false
126
- default :
127
- return true
145
+ func approxHeight( _ detent: PresentationDetent ) -> CGFloat {
146
+ switch detent {
147
+ case . medium: return UIScreen . main. bounds. height * 0.5
148
+ case . large: return UIScreen . main. bounds. height
149
+ case . height( let height) : return height
150
+ }
128
151
}
152
+ return approxHeight ( lhs) < approxHeight ( rhs)
129
153
}
130
154
}
131
155
}
@@ -135,14 +159,15 @@ public extension Backport<Any> {
135
159
private extension Backport < Any > {
136
160
struct Representable : UIViewControllerRepresentable {
137
161
let detents : Set < Backport < Any > . PresentationDetent >
162
+ let largestUndimmed : Backport < Any > . PresentationDetent ?
138
163
let selection : Binding < Backport < Any > . PresentationDetent > ?
139
164
140
165
func makeUIViewController( context: Context ) -> Backport . Representable . Controller {
141
- Controller ( detents: detents, selection: selection)
166
+ Controller ( detents: detents, largestUndimmed : largestUndimmed , selection: selection)
142
167
}
143
168
144
169
func updateUIViewController( _ controller: Backport . Representable . Controller , context: Context ) {
145
- controller. update ( detents: detents, selection: selection)
170
+ controller. update ( detents: detents, largestUndimmed : largestUndimmed , selection: selection)
146
171
}
147
172
}
148
173
}
@@ -156,9 +181,10 @@ private extension Backport.Representable {
156
181
var largestUndimmed : Backport < Any > . PresentationDetent ?
157
182
weak var _delegate : UISheetPresentationControllerDelegate ?
158
183
159
- init ( detents: Set < Backport < Any > . PresentationDetent > , selection: Binding < Backport < Any > . PresentationDetent > ? ) {
184
+ init ( detents: Set < Backport < Any > . PresentationDetent > , largestUndimmed : Backport < Any > . PresentationDetent ? = nil , selection: Binding < Backport < Any > . PresentationDetent > ? ) {
160
185
self . detents = detents
161
186
self . selection = selection
187
+ self . largestUndimmed = largestUndimmed
162
188
super. init ( nibName: nil , bundle: nil )
163
189
}
164
190
@@ -174,41 +200,33 @@ private extension Backport.Representable {
174
200
controller. delegate = self
175
201
}
176
202
}
177
- update ( detents: detents, selection: selection)
203
+ update ( detents: detents, largestUndimmed : largestUndimmed , selection: selection)
178
204
}
179
205
180
206
override func willTransition( to newCollection: UITraitCollection , with coordinator: UIViewControllerTransitionCoordinator ) {
181
207
super. willTransition ( to: newCollection, with: coordinator)
182
- update ( detents: detents, selection: selection)
208
+ update ( detents: detents, largestUndimmed : largestUndimmed , selection: selection)
183
209
}
184
210
185
- func update( detents: Set < Backport < Any > . PresentationDetent > , selection: Binding < Backport < Any > . PresentationDetent > ? ) {
211
+ func update( detents: Set < Backport < Any > . PresentationDetent > , largestUndimmed : Backport < Any > . PresentationDetent ? , selection: Binding < Backport < Any > . PresentationDetent > ? ) {
186
212
self . detents = detents
187
213
self . selection = selection
214
+ self . largestUndimmed = largestUndimmed
188
215
189
216
if let controller = parent? . sheetPresentationController {
190
- controller. animateChanges {
191
- controller. detents = detents. sorted ( ) . map {
192
- switch $0 {
193
- case . medium:
194
- return . medium( )
195
- default :
196
- return . large( )
217
+ DispatchQueue . main. async {
218
+ controller. animateChanges {
219
+ controller. detents = detents. sorted ( ) . map {
220
+ $0. system
197
221
}
198
- }
199
222
200
- if let selection = selection {
201
- controller. selectedDetentIdentifier = . init( selection. wrappedValue. id. rawValue)
202
- }
223
+ controller. largestUndimmedDetentIdentifier = largestUndimmed? . id
203
224
204
- controller. prefersScrollingExpandsWhenScrolledToEdge = true
205
- }
225
+ if let selection = selection {
226
+ controller. selectedDetentIdentifier = . init( selection. wrappedValue. id. rawValue)
227
+ }
206
228
207
- UIView . animate ( withDuration: 0.25 ) {
208
- if let undimmed = controller. largestUndimmedDetentIdentifier {
209
- controller. presentingViewController. view? . tintAdjustmentMode = ( selection? . wrappedValue ?? . large) >= . init( id: . init( rawValue: undimmed. rawValue) ) ? . automatic : . normal
210
- } else {
211
- controller. presentingViewController. view? . tintAdjustmentMode = . automatic
229
+ controller. prefersScrollingExpandsWhenScrolledToEdge = true
212
230
}
213
231
}
214
232
}
@@ -217,11 +235,11 @@ private extension Backport.Representable {
217
235
func sheetPresentationControllerDidChangeSelectedDetentIdentifier( _ sheetPresentationController: UISheetPresentationController ) {
218
236
guard
219
237
let selection = selection,
220
- let id = sheetPresentationController. selectedDetentIdentifier? . rawValue,
221
- selection. wrappedValue. id. rawValue != id
238
+ let id = sheetPresentationController. selectedDetentIdentifier,
239
+ let newSection = Backport< Any> . PresentationDetent( id: id) ,
240
+ selection. wrappedValue != newSection
222
241
else { return }
223
-
224
- selection. wrappedValue = . init( id: . init( rawValue: id) )
242
+ selection. wrappedValue = newSection
225
243
}
226
244
227
245
override func responds( to aSelector: Selector ! ) -> Bool {
0 commit comments