import { ChangeType , Color , EventData , ObservableArray , profile , ProxyViewContainer , StackLayout , View , } from "@nativescript/core" ; import { KeyedTemplate } from "@nativescript/core/ui/core/view" ; import * as types from "@nativescript/core/utils/types" ; import { layout } from "@nativescript/core/utils/utils" ; import * as common from "./pager.common" ; import { autoplayDelayProperty , autoPlayProperty , disableSwipeProperty , Indicator , indicatorColorProperty , indicatorProperty , indicatorSelectedColorProperty , ITEMDISPOSING , ItemEventData , ITEMLOADING , itemsProperty , itemTemplatesProperty , LOADMOREITEMS , Orientation , orientationProperty , PagerBase , selectedIndexProperty , showIndicatorProperty , } from "./pager.common" ; export * from "./pager.common" ; export { ItemsSource , Transformer } from "./pager.common" ; function notifyForItemAtIndex ( owner , nativeView , view , eventName , index ) { let args = { eventName : eventName , object : owner , index : index , view : view , ios : nativeView , android : undefined , } ; owner . notify ( args ) ; return args ; } declare var CHIPageControlAji , CHIPageControlAleppo , CHIPageControlChimayo , CHIPageControlFresno , CHIPageControlJalapeno , CHIPageControlJaloro , CHIPageControlPuya , FancyPager , FancyPagerDelegate ; const main_queue = dispatch_get_current_queue ( ) ; const PFLAG_FORCE_LAYOUT = 1 ; export class Pager extends PagerBase { lastEvent : number = 0 ; private _disableSwipe : boolean = false ; private _disableAnimation : boolean = false ; _layout : any ; _preparingCell : boolean = false ; _delegate : any ; private _dataSource ; _map : Map < PagerCell , View > ; borderRadius : number ; borderWidth : number ; borderColor : string ; backgroundColor : any ; _isDirty : boolean = false ; _isRefreshing : boolean = false ; private _pager : UICollectionView ; private _indicatorView : any ; private _observableArrayInstance : ObservableArray < any > ; _isInit : boolean = false ; constructor ( ) { super ( ) ; this . _map = new Map < PagerCell , View > ( ) ; this . _childrenViews = new Map < number , View > ( ) ; } get pager ( ) { return this . _pager ; } get indicatorView ( ) { return this . _indicatorView ; } createNativeView ( ) { const nativeView = UIView . new ( ) ; this . _layout = UICollectionViewFlowLinearLayoutImpl . initWithOwner ( new WeakRef ( this ) ) ; this . _layout . scrollDirection = UICollectionViewScrollDirection . Horizontal ; this . _layout . minimumInteritemSpacing = 0 ; this . _pager = UICollectionView . alloc ( ) . initWithFrameCollectionViewLayout ( CGRectZero , this . _layout ) ; this . pager . showsHorizontalScrollIndicator = false ; this . pager . showsVerticalScrollIndicator = false ; this . pager . decelerationRate = UIScrollViewDecelerationRateFast ; nativeView . addSubview ( this . pager ) ; return nativeView ; } initNativeView ( ) { super . initNativeView ( ) ; const nativeView = this . pager ; nativeView . registerClassForCellWithReuseIdentifier ( PagerCell . class ( ) , this . _defaultTemplate . key ) ; nativeView . backgroundColor = UIColor . clearColor ; nativeView . autoresizesSubviews = false ; nativeView . autoresizingMask = UIViewAutoresizing . None ; nativeView . dataSource = this . _dataSource = UICollectionViewDataSourceImpl . initWithOwner ( new WeakRef ( this ) ) ; nativeView . scrollEnabled = ! ( String ( this . disableSwipe ) === "true" ) ; if ( this . orientation === "vertical" ) { this . _layout . scrollDirection = UICollectionViewScrollDirection . Vertical ; nativeView . alwaysBounceVertical = true ; nativeView . alwaysBounceHorizontal = false ; } else { this . _layout . scrollDirection = UICollectionViewScrollDirection . Horizontal ; nativeView . alwaysBounceHorizontal = true ; nativeView . alwaysBounceVertical = false ; } this . _setIndicator ( this . indicator ) ; this . _delegate = UICollectionDelegateImpl . initWithOwner ( new WeakRef ( this ) ) ; this . _setNativeClipToBounds ( ) ; this . _initAutoPlay ( this . autoPlay ) ; } _getRealWidthHeight ( ) { let height = 0 ; let width = 0 ; width = ( layout . toDeviceIndependentPixels ( this . _effectiveItemWidth ) - ( this . perPage * 2 * this . _getSpacing ( ) + this . _getPeaking ( ) * 2 ) ) / this . perPage ; height = ( layout . toDeviceIndependentPixels ( this . _effectiveItemHeight ) - ( this . perPage * 2 * this . _getSpacing ( ) + this . _getPeaking ( ) * 2 ) ) / this . perPage ; return { height , width } ; } _nextIndex ( ) { if ( this . circularMode ) { return 0 ; } else { let next = this . selectedIndex + 1 ; if ( next > this . lastIndex ) { return 0 ; } return next ; } } _initAutoPlay ( value ) { if ( ! this . items || this . items . length === 0 ) { return ; } if ( ! value ) { if ( this . _autoPlayInterval ) { clearInterval ( this . _autoPlayInterval ) ; this . _autoPlayInterval = undefined ; } } else { if ( this . isLayoutValid && ! this . _autoPlayInterval ) { this . _autoPlayInterval = setInterval ( ( ) => { this . selectedIndex = this . _nextIndex ( ) ; } , this . autoPlayDelay ) ; } } } getPosition ( index ) { let position = index ; if ( this . circularMode ) { if ( position === 0 ) { position = this . lastDummy ; } else if ( position === this . firstDummy ) { position = 0 ; } else { position = position - 1 ; } } return position ; } get itemCount ( ) : number { return this . _childrenCount ? this . _childrenCount + ( this . circularMode ? 2 : 0 ) : 0 ; } get lastIndex ( ) : number { if ( this . items && this . items . length === 0 ) { return 0 ; } return this . circularMode ? this . itemCount - 3 : this . itemCount - 1 ; } get firstDummy ( ) : number { const count = this . itemCount ; if ( count === 0 ) { return 0 ; } return this . itemCount - 1 ; } get lastDummy ( ) : number { return this . lastIndex ; } get ios ( ) : any { return this . nativeView ; } private _setIndicator ( value ) { if ( this . _indicatorView ) { this . _indicatorView . removeFromSuperview ( ) ; } switch ( value ) { case Indicator . None : this . _indicatorView = CHIPageControlAji . new ( ) ; break ; case Indicator . Worm : this . _indicatorView = CHIPageControlAleppo . new ( ) ; break ; case Indicator . Fill : this . _indicatorView = CHIPageControlChimayo . new ( ) ; break ; case Indicator . Swap : this . _indicatorView = CHIPageControlPuya . new ( ) ; break ; case Indicator . THIN_WORM : this . _indicatorView = CHIPageControlJalapeno . new ( ) ; break ; case Indicator . Flat : this . _indicatorView = CHIPageControlJaloro . new ( ) ; break ; default : break ; } this . _indicatorView . tintColor = UIColor . whiteColor ; this . _indicatorView . currentPageTintColor = UIColor . whiteColor ; } public get _childrenCount ( ) { return this . items ? this . items . length : this . _childrenViews ? this . _childrenViews . size : 0 ; } public itemTemplateUpdated ( oldData , newData ) { } public _setNativeClipToBounds ( ) { this . pager . clipsToBounds = true ; } public [ orientationProperty . setNative ] ( value ) { if ( value === "horizontal" ) { this . _layout . scrollDirection = UICollectionViewScrollDirection . Horizontal ; } else { this . _layout . scrollDirection = UICollectionViewScrollDirection . Vertical ; } } public eachChildView ( callback ) { this . _map . forEach ( ( view , key ) => { callback ( view ) ; } ) ; } _updateScrollPosition ( ) { const view = this . pager as UICollectionView ; const size = this . orientation === "vertical" ? view . contentSize . height : view . contentSize . width ; if ( ! view || size === 0 ) { return ; } this . _scrollToIndexAnimated ( this . selectedIndex , false ) ; } [ selectedIndexProperty . setNative ] ( value ) { if ( this . isLoaded ) { this . scrollToIndexAnimated ( value , ! this . disableAnimation ) ; } } [ itemTemplatesProperty . getDefault ] ( ) { return null ; } [ itemTemplatesProperty . setNative ] ( value ) { this . _itemTemplatesInternal = new Array < KeyedTemplate > ( this . _defaultTemplate ) ; if ( value ) { for ( let i = 0 , length = value . length ; i < length ; i ++ ) { this . pager . registerClassForCellWithReuseIdentifier ( PagerCell . class ( ) , value [ i ] . key ) ; } this . _itemTemplatesInternal = this . _itemTemplatesInternal . concat ( value ) ; } } [ itemsProperty . setNative ] ( value ) { if ( this . indicatorView && value && value . length ) { this . indicatorView . numberOfPages = value . length ; } if ( this . _observableArrayInstance ) { this . _observableArrayInstance . off ( ObservableArray . changeEvent , this . _observableArrayHandler ) ; this . _observableArrayInstance = null ; } if ( value instanceof ObservableArray ) { this . _observableArrayInstance = value as any ; this . _observableArrayInstance . on ( ObservableArray . changeEvent , this . _observableArrayHandler ) ; } else { this . refresh ( ) ; } if ( ! value ) { this . _isInit = false ; } selectedIndexProperty . coerce ( this ) ; } [ autoPlayProperty . setNative ] ( value ) { this . _initAutoPlay ( value ) ; } private _autoPlayInterval : any ; [ autoplayDelayProperty . setNative ] ( value ) { if ( this . _autoPlayInterval ) { clearInterval ( this . _autoPlayInterval ) ; this . _autoPlayInterval = undefined ; this . _initAutoPlay ( this . autoPlay ) ; } } [ showIndicatorProperty . setNative ] ( value ) { if ( ! this . indicatorView ) { this . _setIndicator ( this . indicatorView ) ; } if ( ! this . nativeView ) { return ; } this . indicatorView . center = CGPointMake ( this . nativeView . center . x , this . nativeView . bounds . size . height - this . indicatorView . intrinsicContentSize . height ) ; const hasParent = this . indicatorView . superview ; if ( value ) { if ( ! hasParent ) { this . nativeView . addSubview ( this . indicatorView ) ; } } else { if ( hasParent ) { this . indicatorView . removeFromSuperview ( ) ; } } } private _observableArrayHandler = ( args ) => { if ( ! this . pager ) { return ; } if ( this . indicatorView && this . _observableArrayInstance && this . _observableArrayInstance . length ) { this . indicatorView . numberOfPages = this . _observableArrayInstance . length ; } const collectionView = this . pager as UICollectionView ; if ( collectionView ) { try { let offset = 0 ; collectionView . performBatchUpdatesCompletion ( ( ) => { this . _isRefreshing = true ; const array = [ ] ; switch ( args . action ) { case ChangeType . Add : for ( let i = 0 ; i < args . addedCount ; i ++ ) { array . push ( NSIndexPath . indexPathForRowInSection ( args . index + i , 0 ) ) ; } offset = collectionView . contentSize . width - collectionView . contentOffset . x ; collectionView . insertItemsAtIndexPaths ( array ) ; break ; case ChangeType . Delete : for ( let i = 0 ; i < args . removed . length ; i ++ ) { array . push ( NSIndexPath . indexPathForItemInSection ( args . index + i , 0 ) ) ; } collectionView . deleteItemsAtIndexPaths ( array ) ; break ; case ChangeType . Splice : if ( args . removed && args . removed . length > 0 ) { for ( let i = 0 ; i < args . removed . length ; i ++ ) { array . push ( NSIndexPath . indexPathForRowInSection ( args . index + i , 0 ) ) ; } collectionView . deleteItemsAtIndexPaths ( array ) ; } else { const addedArray = [ ] ; for ( let i = 0 ; i < args . addedCount ; i ++ ) { addedArray . push ( NSIndexPath . indexPathForRowInSection ( args . index + i , 0 ) ) ; } collectionView . insertItemsAtIndexPaths ( addedArray ) ; } break ; case ChangeType . Update : collectionView . reloadItemsAtIndexPaths ( [ NSIndexPath . indexPathForRowInSection ( args . index , 0 ) , ] ) ; break ; default : break ; } this . _initAutoPlay ( this . autoPlay ) ; if ( this . itemCount === 0 ) { this . _isInit = false ; } } , null ) ; } catch ( err ) { } } } ; _onItemsChanged ( oldValue , newValue ) { } _scrollToIndexAnimated ( index , animate ) { if ( ! this . pager ) return ; const contentSize = this . pager . contentSize ; const size = this . orientation === "vertical" ? contentSize . height : contentSize . width ; if ( size === 0 ) { return ; } if ( this . _childrenCount === 0 ) { return ; } let maxMinIndex = - 1 ; const max = this . _childrenCount - 1 ; if ( index < 0 ) { maxMinIndex = 0 ; } else if ( index > max ) { maxMinIndex = max ; } else { maxMinIndex = index ; } if ( maxMinIndex === - 1 ) { maxMinIndex = 0 ; } dispatch_async ( main_queue , ( ) => { this . pager . scrollToItemAtIndexPathAtScrollPositionAnimated ( NSIndexPath . indexPathForItemInSection ( maxMinIndex , 0 ) , this . orientation === "vertical" ? UICollectionViewScrollPosition . CenteredVertically : UICollectionViewScrollPosition . CenteredHorizontally , ! ! animate ) ; selectedIndexProperty . nativeValueChange ( this , maxMinIndex ) ; } ) ; } public scrollToIndexAnimated ( index , animate ) { this . _scrollToIndexAnimated ( index , animate ) ; } private _reset ( ) { if ( ! this . pager ) { return ; } this . pager . reloadData ( ) ; this . pager . collectionViewLayout . invalidateLayout ( ) ; this . _updateScrollPosition ( ) ; } private _refresh ( ) { if ( ! this . pager ) { return ; } if ( this . items instanceof ObservableArray ) { this . pager . performBatchUpdatesCompletion ( ( ) => { this . _reset ( ) ; } , null ) ; } else { this . _reset ( ) ; } } refresh ( ) { dispatch_async ( main_queue , ( ) => { this . _refresh ( ) ; } ) ; } @ profile public onLoaded ( ) { super . onLoaded ( ) ; if ( this . showIndicator && this . indicatorView ) { this . nativeView . addSubview ( this . indicatorView ) ; } if ( ! this . _isDirty ) { this . refresh ( ) ; this . _isDirty = true ; } this . pager . delegate = this . _delegate ; if ( ! this . items && this . _childrenCount > 0 ) { selectedIndexProperty . coerce ( this ) ; this . _updateScrollPosition ( ) ; } } public onUnloaded ( ) { if ( this . pager ) { this . pager . delegate = null ; } super . onUnloaded ( ) ; } public disposeNativeView ( ) { this . _delegate = null ; this . _dataSource = null ; this . _layout = null ; if ( this . _observableArrayInstance ) { this . _observableArrayInstance . off ( ObservableArray . changeEvent , this . _observableArrayHandler ) ; this . _observableArrayInstance = null ; } super . disposeNativeView ( ) ; } [ indicatorProperty . setNative ] ( value ) { this . _setIndicator ( value ) ; } [ indicatorColorProperty . setNative ] ( value ) { if ( this . indicatorView ) { if ( value instanceof Color ) { this . indicatorView . tintColor = value . ios ; } else if ( types . isString ( value ) ) { this . indicatorView . tintColor = new Color ( value ) . ios ; } } } [ indicatorSelectedColorProperty . setNative ] ( value ) { if ( this . indicatorView ) { if ( value instanceof Color ) { this . indicatorView . currentPageTintColor = value . ios ; } else if ( types . isString ( value ) ) { this . indicatorView . currentPageTintColor = new Color ( value ) . ios ; } } } [ disableSwipeProperty . setNative ] ( value ) { if ( this . pager ) { this . pager . scrollEnabled = ! ( String ( value ) === "true" ) ; } this . _disableSwipe = String ( value ) === "true" ; } get disableAnimation ( ) : boolean { return this . _disableAnimation ; } set disableAnimation ( value ) { this . _disableAnimation = value ; } public _removeContainer ( cell , indexPath ? ) { let view = cell . view ; let args = < ItemEventData > { eventName : ITEMDISPOSING , object : this , index : indexPath . row , android : undefined , ios : cell , view : view , } ; this . notify ( args ) ; view = args . view ; if ( view && view . parent ) { if ( ! ( view . parent instanceof Pager ) ) { this . _removeView ( view . parent ) ; } view . parent . _removeView ( view ) ; } this . _map . delete ( cell ) ; } public measure ( widthMeasureSpec , heightMeasureSpec ) { const changed = ( this as any ) . _setCurrentMeasureSpecs ( widthMeasureSpec , heightMeasureSpec ) ; super . measure ( widthMeasureSpec , heightMeasureSpec ) ; let forceLayout = ( this . _privateFlags & PFLAG_FORCE_LAYOUT ) === PFLAG_FORCE_LAYOUT ; if ( changed || forceLayout ) { dispatch_async ( main_queue , ( ) => { if ( ! this . pager ) { return ; } this . pager . reloadData ( ) ; if ( changed ) { this . _updateScrollPosition ( ) ; } this . _initAutoPlay ( this . autoPlay ) ; } ) ; } } public onMeasure ( widthMeasureSpec , heightMeasureSpec ) { super . onMeasure ( widthMeasureSpec , heightMeasureSpec ) ; this . _map . forEach ( ( childView , pagerCell ) => { View . measureChild ( this , childView , childView . _currentWidthMeasureSpec , childView . _currentHeightMeasureSpec ) ; } ) ; } public onLayout ( left , top , right , bottom ) { super . onLayout ( left , top , right , bottom ) ; this . pager . frame = this . nativeView . bounds ; if ( this . indicatorView && this . indicatorView . intrinsicContentSize ) { this . indicatorView . center = CGPointMake ( this . nativeView . center . x , this . nativeView . bounds . size . height - this . indicatorView . intrinsicContentSize . height ) ; } const size = this . _getSize ( ) ; this . _map . forEach ( ( childView , pagerCell ) => { const width = layout . toDevicePixels ( size . width ) ; const height = layout . toDevicePixels ( size . height ) ; View . layoutChild ( this , childView , 0 , 0 , width , height ) ; } ) ; } public requestLayout ( ) { if ( ! this . _preparingCell ) { super . requestLayout ( ) ; } } public _prepareCell ( cell , indexPath ) { try { this . _preparingCell = true ; let view = cell . view ; const template = this . _getItemTemplate ( indexPath . row ) ; if ( ! view ) { view = template . createView ( ) ; if ( ! view && this . _itemViewLoader !== undefined ) { view = this . _itemViewLoader ( this . _getItemTemplateKey ( indexPath . row ) ) ; } } const bindingContext = this . _getDataItem ( indexPath . row ) ; let args = < ItemEventData > { eventName : ITEMLOADING , object : this , index : indexPath . row , android : undefined , ios : cell , view : view , bindingContext , } ; this . notify ( args ) ; view = args . view || this . _getDefaultItemContent ( indexPath . row ) ; if ( view instanceof ProxyViewContainer ) { let sp = new StackLayout ( ) ; sp . addChild ( view ) ; view = sp ; } if ( ! cell . view ) { cell . owner = new WeakRef ( view ) ; } else if ( cell . view !== view ) { this . _map . delete ( cell ) ; this . _removeContainer ( cell , indexPath ) ; ( cell . view . ios as UIView ) . removeFromSuperview ( ) ; cell . owner = new WeakRef ( view ) ; } if ( view ) { view . bindingContext = bindingContext ; } this . _map . set ( cell , view ) ; if ( view && ! view . parent ) { this . _addView ( view ) ; cell . contentView . addSubview ( view . ios ) ; } this . _layoutCell ( view , indexPath ) ; } finally { this . _preparingCell = false ; } } _layoutCell ( cellView , index ) { if ( cellView ) { const size = this . _getSize ( ) ; let width = layout . toDevicePixels ( size . width ) ; let height = layout . toDevicePixels ( size . height ) ; const widthMeasureSpec = layout . makeMeasureSpec ( width , layout . EXACTLY ) ; const heightMeasureSpec = layout . makeMeasureSpec ( height , layout . EXACTLY ) ; const measured = View . measureChild ( this , cellView , widthMeasureSpec , heightMeasureSpec ) ; } } _addChildFromBuilder ( name , value ) { if ( value instanceof common . PagerItem ) { if ( ! this . _childrenViews ) { this . _childrenViews = new Map < number , View > ( ) ; } const count = this . _childrenViews . size ; const keys = Array . from ( this . _childrenViews . keys ( ) ) ; if ( count === 0 ) { this . _childrenViews . set ( this . _childrenCount , value ) ; } else { for ( let i = 0 ; i < count ; i ++ ) { const key = keys [ i ] ; const view = this . _childrenViews . get ( key ) ; if ( i === keys . length - 1 && value !== view ) { this . _childrenViews . set ( this . _childrenCount , value ) ; } } } } } get horizontalOffset ( ) : number { return this . pager ? this . pager . contentOffset . x : 0 ; } get verticalOffset ( ) : number { return this . pager ? this . pager . contentOffset . y : 0 ; } _getSpacing ( ) { return layout . toDeviceIndependentPixels ( this . convertToSize ( this . spacing ) ) ; } _getPeaking ( ) { return layout . toDeviceIndependentPixels ( this . convertToSize ( this . peaking ) ) ; } _getSize ( w = 0 , h = 0 ) { let width = 0 ; let height = 0 ; if ( width === 0 ) { width = layout . toDeviceIndependentPixels ( this . _effectiveItemWidth ) ; } if ( height === 0 ) { height = layout . toDeviceIndependentPixels ( this . _effectiveItemHeight ) ; } if ( this . orientation === "vertical" ) { height = ( height - ( this . _getSpacing ( ) * 2 + this . _getPeaking ( ) * 2 ) ) / this . perPage ; } else { width = ( width - ( this . _getSpacing ( ) * 2 + this . _getPeaking ( ) * 2 ) ) / this . perPage ; } if ( Number . isNaN ( width ) ) { width = 0 ; } if ( Number . isNaN ( height ) ) { height = 0 ; } return { width , height } ; } } @ NativeClass class PagerCell extends UICollectionViewCell { public owner : WeakRef < View > ; public index : NSIndexPath ; public get view ( ) : View { return this . owner ? this . owner . get ( ) : null ; } public static initWithEmptyBackground ( ) { const cell = < PagerCell > PagerCell . new ( ) ; cell . backgroundColor = null ; return cell ; } public willMoveToSuperview ( newSuperview ) { let parent = < Pager > ( this . view ? this . view . parent : null ) ; if ( parent && ! newSuperview ) { parent . _removeContainer ( this , this . index ) ; } } } @ NativeClass @ ObjCClass ( UICollectionViewDelegate , UICollectionViewDelegateFlowLayout ) class UICollectionDelegateImpl extends NSObject implements UICollectionViewDelegate , UICollectionViewDelegateFlowLayout { private _owner : WeakRef < Pager > ; private _startingScrollingOffset ; private _indexOfCellBeforeDragging = 0 ; public static initWithOwner ( owner ) { const delegate = UICollectionDelegateImpl . alloc ( ) . init ( ) as UICollectionDelegateImpl ; delegate . _owner = owner ; delegate . _startingScrollingOffset = CGPointMake ( 0 , 0 ) ; return delegate ; } public collectionViewLayoutInsetForSectionAtIndex ( collectionView , collectionViewLayout , section ) { let owner = this . _owner ? this . _owner . get ( ) : null ; if ( owner ) { const inset = owner . _getSpacing ( ) + owner . _getPeaking ( ) ; if ( owner . orientation === "vertical" ) { return new UIEdgeInsets ( { bottom : inset , left : 0 , right : 0 , top : inset , } ) ; } return new UIEdgeInsets ( { bottom : 0 , left : inset , right : inset , top : 0 , } ) ; } return new UIEdgeInsets ( { bottom : 0 , left : 0 , right : 0 , top : 0 } ) ; } public collectionViewLayoutSizeForItemAtIndexPath ( collectionView , collectionViewLayout , indexPath ) { let owner = this . _owner && this . _owner . get ( ) ; if ( ! owner ) return CGSizeZero ; const size = owner . _getSize ( ) ; return CGSizeMake ( size . width , size . height ) ; } public collectionViewWillDisplayCellForItemAtIndexPath ( collectionView , cell , indexPath ) { const owner = this . _owner && this . _owner . get ( ) ; if ( owner ) { if ( ! owner . _isInit ) { owner . _updateScrollPosition ( ) ; owner . _isInit = true ; } if ( owner . items && indexPath . row === owner . lastIndex - owner . loadMoreCount ) { owner . notify < EventData > ( { eventName : LOADMOREITEMS , object : owner , } ) ; } } if ( cell . preservesSuperviewLayoutMargins ) { cell . preservesSuperviewLayoutMargins = false ; } if ( cell . layoutMargins ) { cell . layoutMargins = UIEdgeInsetsZero ; } } public collectionViewLayoutMinimumLineSpacingForSectionAtIndex ( collectionView , collectionViewLayout , section ) { let owner = this . _owner ? this . _owner . get ( ) : null ; if ( ! owner ) return 0 ; return owner . _getSpacing ( ) ; } public scrollViewWillBeginDragging ( scrollView ) { this . _startingScrollingOffset = scrollView . contentOffset ; let owner = this . _owner && this . _owner . get ( ) ; if ( owner ) { if ( owner . lastEvent === 0 ) { owner . notify ( { eventName : Pager . swipeStartEvent , object : owner , } ) ; owner . lastEvent = 1 ; } } } public scrollViewDidEndScrollingAnimation ( scrollView ) { let owner = this . _owner ? this . _owner . get ( ) : null ; if ( owner ) { owner . notify ( { eventName : Pager . swipeEvent , object : owner , } ) ; } } public scrollViewDidScroll ( scrollView ) { let owner = this . _owner . get ( ) ; if ( owner ) { let width ; let offset ; let size = owner . _getRealWidthHeight ( ) ; let total ; let percent ; if ( owner . orientation === "vertical" ) { width = size . height ; offset = scrollView . contentOffset . y ; total = scrollView . contentSize . height - scrollView . bounds . size . height ; } else { width = size . width ; offset = scrollView . contentOffset . x ; total = scrollView . contentSize . width - scrollView . bounds . size . width ; } percent = offset / total ; let progress = percent * ( owner . itemCount - 1 ) ; if ( owner . indicatorView && owner . indicatorView . setWithProgressAnimated && ! Number . isNaN ( progress ) ) { owner . indicatorView . progress = progress ; } const index = parseInt ( progress . toFixed ( 0 ) , 10 ) ; if ( owner . selectedIndex !== index && ! Number . isNaN ( index ) ) { } owner . notify ( { object : owner , eventName : Pager . scrollEvent , scrollX : owner . horizontalOffset , scrollY : owner . verticalOffset , } ) ; if ( owner . lastEvent === 1 ) { owner . notify ( { eventName : Pager . swipeOverEvent , object : owner , } ) ; owner . lastEvent = 1 ; } } } private _nextIndex : number ; scrollViewDidEndDraggingWillDecelerate ( scrollView , decelerate ) { if ( ! decelerate ) { } } private _getIndex ( scrollView ) { let index = 0 ; const owner = this . _owner && this . _owner . get ( ) ; if ( owner ) { let offset ; let itemSize = owner . _getRealWidthHeight ( ) ; let size ; if ( owner . orientation === "vertical" ) { offset = scrollView . contentOffset . y ; size = itemSize . height ; } else { offset = scrollView . contentOffset . x ; size = itemSize . width ; } index = parseInt ( Number ( offset / size ) . toFixed ( 0 ) , 10 ) ; } return index ; } public scrollViewWillEndDraggingWithVelocityTargetContentOffset ( scrollView , velocity , targetContentOffset ) { let owner = this . _owner ? this . _owner . get ( ) : null ; if ( ! owner ) return ; if ( owner . lastEvent === 1 ) { owner . notify ( { eventName : Pager . swipeEndEvent , object : owner , } ) ; owner . lastEvent = 0 ; } } private _getPointFromAttrOffset ( attribute , target ) { const owner = this . _owner && this . _owner . get ( ) ; if ( owner ) { const x = owner . orientation === "vertical" ? target . x : attribute . frame . origin . x - ( owner . _getSpacing ( ) + owner . _getPeaking ( ) ) ; const y = owner . orientation === "vertical" ? attribute . frame . origin . y - ( owner . _getSpacing ( ) + owner . _getPeaking ( ) ) : target . y ; return CGPointMake ( x , y ) ; } return CGPointZero ; } } @ NativeClass @ ObjCClass ( UICollectionViewDataSource ) class UICollectionViewDataSourceImpl extends NSObject implements UICollectionViewDataSource { _owner : WeakRef < Pager > ; public static initWithOwner ( owner ) { const delegate = UICollectionViewDataSourceImpl . alloc ( ) . init ( ) as UICollectionViewDataSourceImpl ; delegate . _owner = owner ; return delegate ; } public collectionViewCellForItemAtIndexPath ( collectionView , indexPath ) { const owner = this . _owner ? this . _owner . get ( ) : null ; let cell ; let count = 0 ; if ( owner ) { count = owner . _childrenCount ; if ( owner . circularMode ) { count = owner . itemCount ; switch ( indexPath . row ) { case 0 : indexPath = NSIndexPath . indexPathForRowInSection ( owner . lastDummy , 0 ) ; break ; case owner . firstDummy : indexPath = NSIndexPath . indexPathForRowInSection ( 0 , 0 ) ; break ; default : indexPath = NSIndexPath . indexPathForRowInSection ( indexPath . row - 1 , 0 ) ; break ; } } } if ( owner && ! owner . items && count > 0 ) { owner . _preparingCell = true ; const size = owner . _getSize ( ) ; collectionView . registerClassForCellWithReuseIdentifier ( PagerCell . class ( ) , `static-${ indexPath . row }` ) ; cell = collectionView . dequeueReusableCellWithReuseIdentifierForIndexPath ( `static-${ indexPath . row }` , indexPath ) || PagerCell . initWithEmptyBackground ( ) ; cell . index = indexPath ; let view = owner . _childrenViews . get ( indexPath . row ) ; if ( view instanceof ProxyViewContainer ) { let sp = new StackLayout ( ) ; sp . addChild ( view ) ; view = sp ; } if ( ! cell . view ) { cell . owner = new WeakRef ( view ) ; } else if ( cell . view !== view ) { ( cell . view . ios as UIView ) . removeFromSuperview ( ) ; cell . owner = new WeakRef ( view ) ; } if ( view && ! view . parent ) { owner . _addView ( view ) ; cell . contentView . addSubview ( view . ios ) ; } else if ( view && view . ios ) { cell . contentView . addSubview ( view . ios ) ; } owner . _layoutCell ( view , indexPath ) ; let width = layout . toDevicePixels ( size . width ) ; let height = layout . toDevicePixels ( size . height ) ; if ( view && ( view as any ) . isLayoutRequired ) { View . layoutChild ( owner , view , 0 , 0 , width , height ) ; } owner . _preparingCell = false ; return cell ; } const template = owner && owner . _getItemTemplate ( indexPath . row ) ; cell = collectionView . dequeueReusableCellWithReuseIdentifierForIndexPath ( template . key , indexPath ) || PagerCell . initWithEmptyBackground ( ) ; cell . index = indexPath ; if ( owner ) { const size = owner . _getSize ( ) ; owner . _prepareCell ( < PagerCell > cell , indexPath ) ; const cellView = ( cell as PagerCell ) . view ; if ( cellView && cellView . isLayoutRequired ) { View . layoutChild ( owner , cellView , 0 , 0 , layout . toDevicePixels ( size . width ) , layout . toDevicePixels ( size . height ) ) ; } } return cell ; } public collectionViewNumberOfItemsInSection ( collectionView , section ) { const owner = this . _owner ? this . _owner . get ( ) : null ; if ( ! owner ) return 0 ; return owner . circularMode ? owner . itemCount : owner . _childrenCount ; } public numberOfSectionsInCollectionView ( collectionView ) { return 1 ; } } @ NativeClass class UICollectionViewFlowLinearLayoutImpl extends UICollectionViewFlowLayout { _owner : WeakRef < Pager > ; _curl : CATransition ; public static initWithOwner ( owner ) { const layout = UICollectionViewFlowLinearLayoutImpl . new ( ) as UICollectionViewFlowLinearLayoutImpl ; layout . _owner = owner ; layout . _curl = CATransition . animation ( ) ; return layout ; } public layoutAttributesForElementsInRect ( rect ) { let owner = this . _owner ? this . _owner . get ( ) : null ; const originalLayoutAttribute = super . layoutAttributesForElementsInRect ( rect ) ; let visibleLayoutAttributes = [ ] ; if ( owner ) { if ( owner . transformers && owner . transformers . indexOf ( "scale" ) > - 1 ) { const count = originalLayoutAttribute . count ; for ( let i = 0 ; i < count ; i ++ ) { let attributes = originalLayoutAttribute . objectAtIndex ( i ) ; visibleLayoutAttributes [ i ] = attributes ; const frame = attributes . frame ; const width = attributes . frame . size . width * 0.75 ; const height = attributes . frame . size . height * 0.75 ; attributes . frame . size . width = width ; attributes . frame . size . height = height ; const spacing = owner . convertToSize ( owner . spacing ) ; const distance = Math . abs ( this . collectionView . contentOffset . x + this . collectionView . contentInset . left + spacing - frame . origin . x ) ; const scale = Math . min ( Math . max ( 1 - distance / this . collectionView . bounds . size . width , 0.75 ) , 1 ) ; attributes . transform = CGAffineTransformScale ( attributes . transform , 1 , scale ) ; } } else { return originalLayoutAttribute ; } } return < any > visibleLayoutAttributes ; } public shouldInvalidateLayoutForBoundsChange ( newBounds ) { return true ; } public initialLayoutAttributesForAppearingItemAtIndexPath ( itemIndexPath ) { const attrs = super . initialLayoutAttributesForAppearingItemAtIndexPath ( itemIndexPath ) ; attrs . alpha = 1 ; return attrs ; } public finalLayoutAttributesForDisappearingItemAtIndexPath ( itemIndexPath ) { const attrs = super . finalLayoutAttributesForDisappearingItemAtIndexPath ( itemIndexPath ) ; attrs . alpha = 1 ; return attrs ; } targetContentOffsetForProposedContentOffsetWithScrollingVelocity ( proposedContentOffset , velocity ) { let owner = this . _owner ? this . _owner . get ( ) : null ; if ( ! this . collectionView || ! owner ) { return super . targetContentOffsetForProposedContentOffsetWithScrollingVelocity ( proposedContentOffset , velocity ) ; } const size = owner . _getRealWidthHeight ( ) ; if ( this . scrollDirection === UICollectionViewScrollDirection . Horizontal ) { let pageWidth = size . width + this . minimumInteritemSpacing ; let approximatePage = this . collectionView . contentOffset . x / pageWidth ; let currentPage = velocity . x == 0 ? Math . round ( approximatePage ) : velocity . x < 0.0 ? Math . floor ( approximatePage ) : Math . ceil ( approximatePage ) ; let flickVelocity = velocity . x * 0.3 ; let flickedPages = Math . abs ( Math . round ( flickVelocity ) ) <= 1 ? 0 : Math . round ( flickVelocity ) ; const newPageIndex = currentPage + flickedPages ; selectedIndexProperty . nativeValueChange ( owner , Math . max ( newPageIndex , 0 ) ) ; let newHorizontalOffset = newPageIndex * pageWidth - this . collectionView . contentInset . left ; return CGPointMake ( newHorizontalOffset , proposedContentOffset . y ) ; } else { let pageHeight = size . height + this . minimumLineSpacing ; let approximatePage = this . collectionView . contentOffset . y / pageHeight ; let currentPage = velocity . y == 0 ? Math . round ( approximatePage ) : velocity . y < 0.0 ? Math . floor ( approximatePage ) : Math . ceil ( approximatePage ) ; let flickVelocity = velocity . y * 0.3 ; let flickedPages = Math . abs ( Math . round ( flickVelocity ) ) <= 1 ? 0 : Math . round ( flickVelocity ) ; const newPageIndex = currentPage + flickedPages ; selectedIndexProperty . nativeValueChange ( owner , Math . max ( newPageIndex , 0 ) ) ; let newVerticalOffset = newPageIndex * pageHeight - this . collectionView . contentInset . top ; return CGPointMake ( proposedContentOffset . x , newVerticalOffset ) ; } } }