@@ -5,7 +5,11 @@ import { layout } from '@nativescript/core/utils/utils';
5
5
import { BottomSheetOptions } from './bottomsheet' ;
6
6
import { fromObject } from '@nativescript/core/data/observable' ;
7
7
import { applyMixins } from 'nativescript-material-core/core' ;
8
+ import { ios as iosUtils } from '@nativescript/core/utils/utils' ;
9
+ import { ios as iosView } from '@nativescript/core/ui/core/view/view-helper' ;
10
+ import { Page } from '@nativescript/core/ui/page' ;
8
11
12
+ const majorVersion = iosUtils . MajorVersion ;
9
13
class MDCBottomSheetControllerDelegateImpl extends NSObject implements MDCBottomSheetControllerDelegate {
10
14
public static ObjCProtocols = [ MDCBottomSheetControllerDelegate ] ;
11
15
@@ -50,101 +54,241 @@ declare module '@nativescript/core/ui/core/view/view' {
50
54
}
51
55
}
52
56
}
57
+ declare module '@nativescript/core/ui/core/view-base' {
58
+ interface ViewBase {
59
+ _layoutParent ( ) ;
60
+ }
61
+ }
62
+
63
+ function initLayoutGuide ( controller : UIViewController ) {
64
+ const rootView = controller . view ;
65
+ const layoutGuide = UILayoutGuide . alloc ( ) . init ( ) ;
66
+ rootView . addLayoutGuide ( layoutGuide ) ;
67
+ NSLayoutConstraint . activateConstraints ( < any > [
68
+ layoutGuide . topAnchor . constraintEqualToAnchor ( controller . topLayoutGuide . bottomAnchor ) ,
69
+ layoutGuide . bottomAnchor . constraintEqualToAnchor ( controller . bottomLayoutGuide . topAnchor ) ,
70
+ layoutGuide . leadingAnchor . constraintEqualToAnchor ( rootView . leadingAnchor ) ,
71
+ layoutGuide . trailingAnchor . constraintEqualToAnchor ( rootView . trailingAnchor )
72
+ ] ) ;
73
+
74
+ return layoutGuide ;
75
+ }
76
+ function layoutView ( controller : UILayoutViewController , owner : View ) : void {
77
+ let layoutGuide = controller . view . safeAreaLayoutGuide ;
78
+ if ( ! layoutGuide ) {
79
+ traceWrite ( `safeAreaLayoutGuide during layout of ${ owner } . Creating fallback constraints, but layout might be wrong.` , traceCategories . Layout , traceMessageType . error ) ;
80
+
81
+ layoutGuide = initLayoutGuide ( controller ) ;
82
+ }
83
+ const safeArea = layoutGuide . layoutFrame ;
84
+ let position = ios . getPositionFromFrame ( safeArea ) ;
85
+ const safeAreaSize = safeArea . size ;
86
+
87
+ const hasChildViewControllers = controller . childViewControllers . count > 0 ;
88
+ if ( hasChildViewControllers ) {
89
+ const fullscreen = controller . view . frame ;
90
+ position = ios . getPositionFromFrame ( fullscreen ) ;
91
+ }
92
+
93
+ const safeAreaWidth = layout . round ( layout . toDevicePixels ( safeAreaSize . width ) ) ;
94
+ const safeAreaHeight = layout . round ( layout . toDevicePixels ( safeAreaSize . height ) ) ;
95
+
96
+ const widthSpec = layout . makeMeasureSpec ( safeAreaWidth , layout . EXACTLY ) ;
97
+ const heightSpec = layout . makeMeasureSpec ( safeAreaHeight , layout . UNSPECIFIED ) ;
98
+
99
+ View . measureChild ( null , owner , widthSpec , heightSpec ) ;
100
+ const marginTop = owner . effectiveMarginTop ;
101
+ const marginBottom = owner . effectiveMarginBottom ;
102
+ const marginLeft = owner . effectiveMarginLeft + position . left ;
103
+ const marginRight = owner . effectiveMarginRight ;
104
+ let top = marginTop + position . top ;
105
+ const width = owner . getMeasuredWidth ( ) ;
106
+ let height = owner . getMeasuredHeight ( ) ;
107
+
108
+ owner . iosOverflowSafeArea = false ;
109
+
110
+ View . layoutChild ( null , owner , position . left , position . top , position . left + width , position . top + height ) ;
111
+
112
+ const effectiveWidth = width + marginLeft + marginRight ;
113
+ let effectiveHeight = height + top + marginBottom ;
114
+ if ( controller . ignoreTopSafeArea || controller . ignoreBottomSafeArea ) {
115
+ const frame = owner . nativeViewProtected . frame ;
116
+ const availableSpace = getAvailableSpaceFromParent ( owner , frame ) ;
117
+ // const safeArea = availableSpace.safeArea;
118
+ // const fullscreen = availableSpace.fullscreen;
119
+ // const inWindow = availableSpace.inWindow;
120
+
121
+ const position = ios . getPositionFromFrame ( frame ) ;
122
+ const fullscreenPosition = ios . getPositionFromFrame ( availableSpace . fullscreen ) ;
123
+ const safeAreaPosition = ios . getPositionFromFrame ( availableSpace . safeArea ) ;
124
+
125
+ const adjustedPosition = position ;
126
+
127
+ if ( controller . ignoreTopSafeArea ) {
128
+ const delta = safeAreaPosition . top - fullscreenPosition . top ;
129
+ effectiveHeight -= delta ;
130
+ adjustedPosition . bottom -= delta ;
131
+ adjustedPosition . top -= delta ;
132
+ }
133
+ if ( controller . ignoreBottomSafeArea ) {
134
+ const delta = fullscreenPosition . bottom - safeAreaPosition . bottom ;
135
+ effectiveHeight -= delta ;
136
+ // adjustedPosition.bottom += delta * 2;
137
+ }
138
+ owner . nativeViewProtected . frame = CGRectMake (
139
+ layout . toDeviceIndependentPixels ( adjustedPosition . left ) ,
140
+ layout . toDeviceIndependentPixels ( adjustedPosition . top ) ,
141
+ layout . toDeviceIndependentPixels ( adjustedPosition . right - adjustedPosition . left ) ,
142
+ layout . toDeviceIndependentPixels ( adjustedPosition . bottom - adjustedPosition . top )
143
+ ) ;
144
+ }
145
+ controller . preferredContentSize = CGSizeMake ( layout . toDeviceIndependentPixels ( effectiveWidth ) , layout . toDeviceIndependentPixels ( effectiveHeight ) ) ;
146
+
147
+ if ( owner . parent ) {
148
+ owner . parent . _layoutParent ( ) ;
149
+ }
150
+ }
151
+ function getAvailableSpaceFromParent ( view : View , frame : CGRect ) : { safeArea : CGRect ; fullscreen : CGRect ; inWindow : CGRect } {
152
+ if ( ! view ) {
153
+ return null ;
154
+ }
155
+
156
+ let scrollView = null ;
157
+ let viewControllerView = null ;
53
158
54
- class BottomSheetUILayoutViewController extends UIViewController {
159
+ if ( view . viewController ) {
160
+ viewControllerView = view . viewController . view ;
161
+ } else {
162
+ let parent = view . parent as View ;
163
+ while ( parent && ! parent . viewController && ! ( parent . nativeViewProtected instanceof UIScrollView ) ) {
164
+ parent = parent . parent as View ;
165
+ }
166
+
167
+ if ( parent . nativeViewProtected instanceof UIScrollView ) {
168
+ scrollView = parent . nativeViewProtected ;
169
+ } else if ( parent . viewController ) {
170
+ viewControllerView = parent . viewController . view ;
171
+ }
172
+ }
173
+
174
+ let fullscreen = null ;
175
+ let safeArea = null ;
176
+
177
+ if ( viewControllerView ) {
178
+ safeArea = viewControllerView . safeAreaLayoutGuide . layoutFrame ;
179
+ fullscreen = viewControllerView . frame ;
180
+ } else if ( scrollView ) {
181
+ const insets = scrollView . safeAreaInsets ;
182
+ safeArea = CGRectMake ( insets . left , insets . top , scrollView . contentSize . width - insets . left - insets . right , scrollView . contentSize . height - insets . top - insets . bottom ) ;
183
+ fullscreen = CGRectMake ( 0 , 0 , scrollView . contentSize . width , scrollView . contentSize . height ) ;
184
+ }
185
+
186
+ const locationInWindow = view . getLocationInWindow ( ) ;
187
+ let inWindowLeft = locationInWindow . x ;
188
+ let inWindowTop = locationInWindow . y ;
189
+
190
+ if ( scrollView ) {
191
+ inWindowLeft += scrollView . contentOffset . x ;
192
+ inWindowTop += scrollView . contentOffset . y ;
193
+ }
194
+
195
+ const inWindow = CGRectMake ( inWindowLeft , inWindowTop , frame . size . width , frame . size . height ) ;
196
+
197
+ return { safeArea : safeArea , fullscreen : fullscreen , inWindow : inWindow } ;
198
+ }
199
+
200
+ class UILayoutViewController extends UIViewController {
55
201
public owner : WeakRef < View > ;
56
202
ignoreBottomSafeArea : boolean ;
57
203
ignoreTopSafeArea : boolean ;
58
204
59
- public static initWithOwner ( owner : WeakRef < View > ) : BottomSheetUILayoutViewController {
60
- const controller = < BottomSheetUILayoutViewController > BottomSheetUILayoutViewController . new ( ) ;
205
+ public static initWithOwner ( owner : WeakRef < View > ) : UILayoutViewController {
206
+ const controller = < UILayoutViewController > UILayoutViewController . new ( ) ;
61
207
controller . owner = owner ;
62
- controller . ignoreBottomSafeArea = false ;
63
- controller . ignoreTopSafeArea = true ;
208
+
64
209
return controller ;
65
210
}
66
211
67
- public viewDidLayoutSubviews ( ) : void {
68
- super . viewDidLayoutSubviews ( ) ;
212
+ public viewDidLoad ( ) : void {
213
+ super . viewDidLoad ( ) ;
214
+
215
+ // Unify translucent and opaque bars layout
216
+ // this.edgesForExtendedLayout = UIRectEdgeBottom;
217
+ this . extendedLayoutIncludesOpaqueBars = true ;
218
+ }
219
+
220
+ public viewWillLayoutSubviews ( ) : void {
221
+ super . viewWillLayoutSubviews ( ) ;
69
222
const owner = this . owner . get ( ) ;
70
223
if ( owner ) {
71
- this . layoutView ( this , owner ) ;
224
+ iosView . updateConstraints ( this , owner ) ;
72
225
}
73
226
}
74
227
75
- public viewWillAppear ( animated : boolean ) : void {
76
- super . viewWillAppear ( animated ) ;
228
+ public viewDidLayoutSubviews ( ) : void {
229
+ super . viewDidLayoutSubviews ( ) ;
77
230
const owner = this . owner . get ( ) ;
78
- if ( ! owner ) {
79
- return ;
80
- }
231
+ if ( owner ) {
232
+ if ( majorVersion >= 11 ) {
233
+ // Handle nested UILayoutViewController safe area application.
234
+ // Currently, UILayoutViewController can be nested only in a TabView.
235
+ // The TabView itself is handled by the OS, so we check the TabView's parent (usually a Page, but can be a Layout).
236
+ const tabViewItem = owner . parent ;
237
+ const tabView = tabViewItem && tabViewItem . parent ;
238
+ let parent = tabView && tabView . parent ;
239
+
240
+ // Handle Angular scenario where TabView is in a ProxyViewContainer
241
+ // It is possible to wrap components in ProxyViewContainers indefinitely
242
+ // Not using instanceof ProxyViewContainer to avoid circular dependency
243
+ // TODO: Try moving UILayoutViewController out of view module
244
+ while ( parent && ! parent . nativeViewProtected ) {
245
+ parent = parent . parent ;
246
+ }
247
+ const additionalInsets = { top : 0 , left : 0 , bottom : 0 , right : 0 } ;
81
248
82
- ios . updateAutoAdjustScrollInsets ( this , owner ) ;
249
+ if ( parent ) {
250
+ const parentPageInsetsTop = parent . nativeViewProtected . safeAreaInsets . top ;
251
+ const currentInsetsTop = this . view . safeAreaInsets . top ;
252
+ const additionalInsetsTop = Math . max ( parentPageInsetsTop - currentInsetsTop , 0 ) ;
83
253
84
- if ( ! owner . parent ) {
85
- owner . callLoaded ( ) ;
86
- }
87
- }
254
+ const parentPageInsetsBottom = parent . nativeViewProtected . safeAreaInsets . bottom ;
255
+ const currentInsetsBottom = this . view . safeAreaInsets . bottom ;
256
+ const additionalInsetsBottom = Math . max ( parentPageInsetsBottom - currentInsetsBottom , 0 ) ;
88
257
89
- layoutView ( controller : UIViewController , owner : View ) : void {
90
- // the safe area of the controller is not correct. I think materialcomponents ios is changing the safeArea of the controller
91
- // let s look at the app root controller to get fulllscreen safe area
92
- let layoutGuide = controller . view . safeAreaLayoutGuide ;
93
- const fullscreen = controller . view . frame ;
94
- const safeArea = layoutGuide . layoutFrame ;
95
- let safeAreaPosition = ios . getPositionFromFrame ( safeArea ) ;
96
- const safeAreaSize = safeArea . size ;
258
+ if ( additionalInsetsTop > 0 || additionalInsetsBottom > 0 ) {
259
+ additionalInsets . top = additionalInsetsTop ;
260
+ additionalInsets . bottom = additionalInsetsBottom ;
261
+ }
262
+ }
263
+ // if (this.ignoreTopSafeArea === true) {
264
+ // console.log('ignoreTopSafeArea', additionalInsets.top, this.view.safeAreaLayoutGuide.layoutFrame.origin.x, this.view.safeAreaInsets.top);
265
+ // additionalInsets.top += this.view.safeAreaLayoutGuide.layoutFrame.origin.x;
266
+ // }
97
267
98
- const hasChildViewControllers = controller . childViewControllers . count > 0 ;
99
- if ( hasChildViewControllers ) {
100
- safeAreaPosition = ios . getPositionFromFrame ( fullscreen ) ;
101
- }
268
+ // if (this.ignoreBottomSafeArea === true) {
269
+ // additionalInsets.bottom -= this.view.safeAreaInsets.bottom;
270
+ // }
102
271
103
- const safeAreaWidth = layout . round ( layout . toDevicePixels ( safeAreaSize . width ) ) ;
104
- const safeAreaHeight = layout . round ( layout . toDevicePixels ( safeAreaSize . height ) ) ;
105
-
106
- const widthSpec = layout . makeMeasureSpec ( safeAreaWidth , layout . EXACTLY ) ;
107
- const heightSpec = layout . makeMeasureSpec ( safeAreaHeight , layout . UNSPECIFIED ) ;
108
-
109
- View . measureChild ( null , owner , widthSpec , heightSpec ) ;
110
- const marginTop = owner . effectiveMarginTop ;
111
- const marginBottom = owner . effectiveMarginBottom ;
112
- const marginLeft = owner . effectiveMarginLeft + safeAreaPosition . left ;
113
- const marginRight = owner . effectiveMarginRight ;
114
- let top = marginTop ;
115
- const width = owner . getMeasuredWidth ( ) ;
116
- let height = owner . getMeasuredHeight ( ) ;
117
- if ( ! this . ignoreTopSafeArea ) {
118
- top += safeAreaPosition . top ;
119
- }
120
- const effectiveWidth = width + marginLeft + marginRight ;
121
- let effectiveHeight = height + top + marginBottom ;
122
- if ( this . ignoreBottomSafeArea ) {
123
- effectiveHeight -= ios . getPositionFromFrame ( fullscreen ) . bottom - safeAreaPosition . bottom ;
124
- }
125
- View . layoutChild ( null , owner , marginLeft , top , width + marginLeft , height + top ) ;
126
- this . preferredContentSize = CGSizeMake ( layout . toDeviceIndependentPixels ( effectiveWidth ) , layout . toDeviceIndependentPixels ( effectiveHeight ) ) ;
272
+ const insets = new UIEdgeInsets ( additionalInsets ) ;
273
+ this . additionalSafeAreaInsets = insets ;
274
+ }
127
275
128
- this . layoutParent ( owner . parent ) ;
276
+ layoutView ( this , owner ) ;
277
+ }
129
278
}
130
279
131
- layoutParent ( view : ViewBase ) : void {
132
- if ( ! view ) {
280
+ public viewWillAppear ( animated : boolean ) : void {
281
+ super . viewWillAppear ( animated ) ;
282
+ const owner = this . owner . get ( ) ;
283
+ if ( ! owner ) {
133
284
return ;
134
285
}
135
286
136
- if ( view instanceof View && view . nativeViewProtected ) {
137
- const frame = view . nativeViewProtected . frame ;
138
- const origin = frame . origin ;
139
- const size = frame . size ;
140
- const left = layout . toDevicePixels ( origin . x ) ;
141
- const top = layout . toDevicePixels ( origin . y ) ;
142
- const width = layout . toDevicePixels ( size . width ) ;
143
- const height = layout . toDevicePixels ( size . height ) ;
144
- view . _setLayoutFlags ( left , top , width + left , height + top ) ;
145
- }
287
+ iosView . updateAutoAdjustScrollInsets ( this , owner ) ;
146
288
147
- this . layoutParent ( view . parent ) ;
289
+ if ( ! owner . parent ) {
290
+ owner . callLoaded ( ) ;
291
+ }
148
292
}
149
293
150
294
public viewDidDisappear ( animated : boolean ) : void {
@@ -154,6 +298,22 @@ class BottomSheetUILayoutViewController extends UIViewController {
154
298
owner . callUnloaded ( ) ;
155
299
}
156
300
}
301
+
302
+ // Mind implementation for other controllers
303
+ public traitCollectionDidChange ( previousTraitCollection : UITraitCollection ) : void {
304
+ super . traitCollectionDidChange ( previousTraitCollection ) ;
305
+
306
+ if ( majorVersion >= 13 ) {
307
+ const owner = this . owner . get ( ) ;
308
+ if (
309
+ owner &&
310
+ this . traitCollection . hasDifferentColorAppearanceComparedToTraitCollection &&
311
+ this . traitCollection . hasDifferentColorAppearanceComparedToTraitCollection ( previousTraitCollection )
312
+ ) {
313
+ owner . notify ( { eventName : 'traitCollectionColorAppearanceChanged' , object : owner } ) ;
314
+ }
315
+ }
316
+ }
157
317
}
158
318
159
319
export class ViewWithBottomSheet extends ViewWithBottomSheetBase {
@@ -177,19 +337,23 @@ export class ViewWithBottomSheet extends ViewWithBottomSheetBase {
177
337
traceWrite ( 'Parent page is not part of the window hierarchy.' , traceCategories . ViewHierarchy , traceMessageType . error ) ;
178
338
return ;
179
339
}
180
-
181
340
this . _setupAsRootView ( { } ) ;
182
341
183
342
this . _commonShowNativeBottomSheet ( parentWithController , options ) ;
184
- let controller : BottomSheetUILayoutViewController = this . viewController ;
343
+ let controller : UILayoutViewController = this . viewController ;
185
344
if ( ! controller ) {
186
345
const nativeView = this . ios || this . nativeViewProtected ;
187
- controller = BottomSheetUILayoutViewController . initWithOwner ( new WeakRef ( this ) ) ;
346
+ controller = UILayoutViewController . initWithOwner ( new WeakRef ( this ) ) ;
347
+ // newController = iosView.UILayoutViewController.initWithOwner(new WeakRef(item.content)) as UIViewController;
188
348
if ( options . ignoreBottomSafeArea !== undefined ) {
189
349
controller . ignoreBottomSafeArea = options . ignoreBottomSafeArea ;
350
+ } else {
351
+ controller . ignoreBottomSafeArea = false ;
190
352
}
191
353
if ( options . ignoreTopSafeArea !== undefined ) {
192
354
controller . ignoreTopSafeArea = options . ignoreTopSafeArea ;
355
+ } else {
356
+ controller . ignoreTopSafeArea = true ;
193
357
}
194
358
if ( nativeView instanceof UIView ) {
195
359
controller . view . addSubview ( nativeView ) ;
@@ -220,12 +384,11 @@ export class ViewWithBottomSheet extends ViewWithBottomSheetBase {
220
384
( < any > controller ) . animated = true ;
221
385
parentController . presentViewControllerAnimatedCompletion ( bottomSheet , true , null ) ;
222
386
if ( options . transparent === true ) {
223
- bottomSheet . view . backgroundColor = UIColor . clearColor ;
387
+ controller . view . backgroundColor = UIColor . clearColor ;
224
388
// for it to be more beautiful let s disable elevation
225
- bottomSheet . view [ 'elevation' ] = 0 ;
226
- } else {
227
- // this.backgroundColor = 'white';
228
- bottomSheet . view . backgroundColor = UIColor . whiteColor ;
389
+ controller . view [ 'elevation' ] = 0 ;
390
+ } else if ( ! ( this instanceof Page ) ) {
391
+ controller . view . backgroundColor = majorVersion <= 12 && ! UIColor . systemBackgroundColor ? UIColor . whiteColor : UIColor . systemBackgroundColor ;
229
392
}
230
393
const transitionCoordinator = bottomSheet . transitionCoordinator ;
231
394
if ( transitionCoordinator ) {
0 commit comments