You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ac...@apache.org on 2018/04/26 12:40:01 UTC

[09/16] incubator-weex git commit: [WEEX-311] [iOS] use new layoutEngin to replace yoga

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m
deleted file mode 100644
index 5bfee0a..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m
+++ /dev/null
@@ -1,1803 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#import "WXSliderNeighborComponent.h"
-#import "WXConvert.h"
-#import "WXUtility.h"
-#import "WXComponent_internal.h"
-#import "WXIndicatorComponent.h"
-#import "WXSDKInstance.h"
-#import "NSTimer+Weex.h"
-
-#define MAX_VISIBLE_ITEMS 30
-#define MIN_TOGGLE_DURATION 0.2
-#define MAX_TOGGLE_DURATION 0.4
-#define SCROLL_DURATION 0.4
-#define INSERT_DURATION 0.4
-#define DECELERATE_THRESHOLD 0.1
-#define SCROLL_SPEED_THRESHOLD 2.0
-#define SCROLL_DISTANCE_THRESHOLD 0.1
-#define DECELERATION_MULTIPLIER 30.0
-#define FLOAT_ERROR_MARGIN 0.000001
-
-@protocol WXSliderNeighborDelegate, WXSliderNeighborDataSource;
-
-@interface WXSliderNeighborView: UIView
-@property (nonatomic, strong) WXIndicatorView *indicator;
-@property (nonatomic, weak) id<WXSliderNeighborDelegate> delegate;
-@property (nonatomic, weak) id<WXSliderNeighborDataSource> dataSource;
-@property (nonatomic, assign) CGFloat perspective;
-@property (nonatomic, readonly, getter = isDragging) BOOL dragging;
-@property (nonatomic, readonly, getter = isScrolling) BOOL scrolling;
-@property (nonatomic, assign) CGFloat autoscroll;
-@property (nonatomic, assign) CGFloat scrollOffset;
-@property (nonatomic, assign) CGFloat previousTranslation;
-@property (nonatomic, assign, getter = isVertical) BOOL vertical;
-@property (nonatomic, assign) CGFloat decelerationRate;
-@property (nonatomic, assign, getter = isScrollEnabled) BOOL scrollEnabled;
-@property (nonatomic, assign, getter = isPagingEnabled) BOOL pagingEnabled;
-@property (nonatomic, assign) BOOL bounces;
-@property (nonatomic, assign) BOOL didDrag;
-@property (nonatomic, readonly, getter = isDecelerating) BOOL decelerating;
-@property (nonatomic, strong, readonly) UIView *contentView;
-@property (nonatomic, strong) NSMutableDictionary *itemViews;
-@property (nonatomic, readonly) NSInteger numberOfVisibleItems;
-@property (nonatomic, readonly) NSInteger numberOfItems;
-@property (nonatomic, readonly) NSInteger numberOfPlaceholders;
-@property (nonatomic, strong) NSMutableSet *itemViewPool;
-@property (nonatomic, strong) NSMutableSet *placeholderViewPool;
-@property (nonatomic, assign) NSTimeInterval startTime;
-@property (nonatomic, assign) NSTimeInterval lastTime;
-@property (nonatomic, assign) CGFloat startVelocity;
-@property (nonatomic, assign) CGFloat offsetMultiplier;
-@property (nonatomic, assign) CGFloat startOffset;
-@property (nonatomic, assign) CGFloat endOffset;
-@property (nonatomic, assign) CGSize contentOffset;
-@property (nonatomic, assign) CGSize viewpointOffset;
-@property (nonatomic, assign) CGFloat scrollSpeed;
-@property (nonatomic, assign) CGFloat bounceDistance;
-@property (nonatomic, assign) BOOL stopAtItemBoundary;
-@property (nonatomic, assign) BOOL scrollToItemBoundary;
-@property (nonatomic, assign) BOOL centerItemWhenSelected;
-@property (nonatomic, assign) NSTimeInterval scrollDuration;
-@property (nonatomic, readonly, getter = isWrapEnabled) BOOL wrapEnabled;
-@property (nonatomic, strong) NSTimer *timer;
-@property (nonatomic, assign) NSInteger numberOfPlaceholdersToShow;
-@property (nonatomic, assign) CGFloat previousScrollOffset;
-@property (nonatomic, assign) NSTimeInterval toggleTime;
-@property (nonatomic, readonly) CGFloat toggle;
-@property (nonatomic, assign) NSInteger previousItemIndex;
-@property (nonatomic, readonly) CGFloat itemWidth;
-@property (nonatomic, assign) BOOL inited;
-@end
-
-@implementation WXSliderNeighborView
-
-- (instancetype)init {
-    
-    self = [super init];
-    if (!self) {
-    }
-    _decelerationRate = 0.01;
-    _scrollEnabled = YES;
-    _bounces = YES;
-    _offsetMultiplier = 1.0;
-    _perspective = -1.0/500.0;
-    _contentOffset = CGSizeZero;
-    _viewpointOffset = CGSizeZero;
-    _scrollSpeed = 1.0;
-    _bounceDistance = 1.0;
-    _stopAtItemBoundary = YES;
-    _scrollToItemBoundary = YES;
-    _centerItemWhenSelected = YES;
-    _inited = NO;
-    
-    _contentView = [[UIView alloc] initWithFrame:self.bounds];
-    
-    //pan gesture recognizer
-    self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
-    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)];
-    panGesture.delegate = (id <UIGestureRecognizerDelegate>)self;
-    [_contentView addGestureRecognizer:panGesture];
-    
-    //tap gesture recognizer
-    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)];
-    tapGesture.delegate = (id <UIGestureRecognizerDelegate>)self;
-    [_contentView addGestureRecognizer:tapGesture];
-    
-    self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
-    self.isAccessibilityElement = YES;
-    
-    [self addSubview:_contentView];
-    
-    if (_dataSource) {
-        [self reloadData];
-    }
-    
-    return self;
-}
-
-- (void)dealloc
-{
-    [self stopAnimation];
-}
-
-- (void)accessibilityIncrement
-{
-    [self setCurrentItemIndex: [self lastItemIndex]];
-}
-
-- (void)accessibilityDecrement
-{
-    [self setCurrentItemIndex:[self nextItemIndex]];
-}
-
-- (void)setDataSource:(id<WXSliderNeighborDataSource>)dataSource
-{
-    if (_dataSource != dataSource) {
-        _dataSource = dataSource;
-        if (_dataSource) {
-            [self reloadData];
-        }
-    }
-}
-
-- (void)setDelegate:(id<WXSliderNeighborDelegate>)delegate
-{
-    if (_delegate != delegate) {
-        _delegate = delegate;
-        if (_delegate && _dataSource) {
-            [self setNeedsLayout];
-        }
-    }
-}
-
-- (void)setVertical:(BOOL)vertical
-{
-    if (_vertical != vertical)
-    {
-        _vertical = vertical;
-        [self layOutItemViews];
-    }
-}
-
-#pragma clang diagnostic push
-#pragma GCC diagnostic ignored "-Wundeclared-selector"
-- (void)setCurrentItemIndex:(NSInteger)currentItemIndex
-{
-    if ([self currentItemIndex] == currentItemIndex) return;
-    
-    [self setScrollOffset:currentItemIndex];
-    [self.indicator setCurrentPoint:currentItemIndex];
-    
-    if (self.delegate && [self.delegate respondsToSelector:@selector(sliderView:didScrollToItemAtIndex:)]) {
-        [self.delegate sliderNeighbor:self didScrollToItemAtIndex:currentItemIndex];
-    }
-    [self scroll2ItemViewAtIndex:currentItemIndex animated:YES];
-    
-}
-#pragma clang diagnostic pop
-
-- (void)updateItemWidth
-{
-    _itemWidth = [_delegate sliderNeighborItemWidth:self] ?: _itemWidth;
-    if (_numberOfItems > 0) {
-        if ([_itemViews count] == 0) {
-            [self loadViewAtIndex:0];
-        }
-    } else if (_numberOfPlaceholders > 0) {
-        if ([_itemViews count] == 0) {
-            [self loadViewAtIndex:-1];
-        }
-    }
-}
-
-- (NSInteger)circularCarouselItemCount
-{
-    NSInteger count = 0;
-    CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
-    CGFloat width = _vertical ? self.bounds.size.height: self.bounds.size.width;
-    count = MIN(MAX_VISIBLE_ITEMS, MAX(12, ceil(width / (spacing * _itemWidth)) * M_PI));
-    count = MIN(_numberOfItems + _numberOfPlaceholdersToShow, count);
-    return [self valueForOption:WXSliderNeighborOptionCount withDefault:count];
-}
-
-- (void)updateNumberOfVisibleItems
-{
-    //get number of visible items
-    //based on count value
-    CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
-    CGFloat width = _vertical ? self.bounds.size.height: self.bounds.size.width;
-    CGFloat itemWidth = _itemWidth * spacing;
-    _numberOfVisibleItems = ceil(width / itemWidth) + 2;
-    _numberOfVisibleItems = MIN(MAX_VISIBLE_ITEMS, _numberOfVisibleItems);
-    _numberOfVisibleItems = [self valueForOption:WXSliderNeighborOptionVisibleItems withDefault:_numberOfVisibleItems];
-    _numberOfVisibleItems = MAX(0, MIN(_numberOfVisibleItems, _numberOfItems + _numberOfPlaceholdersToShow));
-    
-}
-
-- (CGFloat)offsetForItemAtIndex:(NSInteger)index
-{
-    //calculate relative position
-    CGFloat offset = index - _scrollOffset;
-    if (_wrapEnabled) {
-        if (offset > _numberOfItems/2.0) {
-            offset -= _numberOfItems;
-        } else if (offset < -_numberOfItems/2.0) {
-            offset += _numberOfItems;
-        }
-    }
-    
-    return offset;
-}
-
-- (CGFloat)alphaForItemWithOffset:(CGFloat)offset
-{
-    CGFloat fadeMin = -INFINITY;
-    CGFloat fadeMax = INFINITY;
-    CGFloat fadeRange = 1.0;
-    CGFloat fadeMinAlpha = 0.0;
-    fadeMin = [self valueForOption:WXSliderNeighborOptionFadeMin withDefault:fadeMin];
-    fadeMax = [self valueForOption:WXSliderNeighborOptionFadeMax withDefault:fadeMax];
-    fadeRange = [self valueForOption:WXSliderNeighborOptionFadeRange withDefault:fadeRange];
-    fadeMinAlpha = [self valueForOption:WXSliderNeighborOptionFadeMinAlpha withDefault:fadeMinAlpha];
-    
-    CGFloat factor = 0.0;
-    if (offset > fadeMax) {
-        factor = offset - fadeMax;
-    } else if (offset < fadeMin) {
-        factor = fadeMin - offset;
-    }
-    return 1.0 - MIN(factor, fadeRange) / fadeRange * (1.0 - fadeMinAlpha);
-}
-
-
-- (UIView *)containView:(UIView *)view
-{
-    //set item width
-    if (!_itemWidth) {
-        _itemWidth = _vertical? view.frame.size.height: view.frame.size.width;
-    }
-    
-    //set container frame
-    CGRect frame = view.frame;
-    frame.size.width = _vertical? frame.size.width: _itemWidth;
-    frame.size.height = _vertical? _itemWidth: frame.size.height;
-    UIView *containerView = [[UIView alloc] initWithFrame:frame];
-    
-    //set view frame
-    frame = view.frame;
-    frame.origin.x = (containerView.bounds.size.width - frame.size.width) / 2.0;
-    frame.origin.y = (containerView.bounds.size.height - frame.size.height) / 2.0;
-    view.frame = frame;
-    [containerView addSubview:view];
-    containerView.layer.opacity = 0;
-    containerView.transform = CGAffineTransformMakeScale(0.8, 1.0);
-    return containerView;
-}
-
-- (void)transformItemViews
-{
-    for (NSNumber *number in _itemViews)
-    {
-        NSInteger index = [number integerValue];
-        UIView *view = _itemViews[number];
-        [self transformItemView:view atIndex:index];
-    }
-}
-
-- (CATransform3D)transformForItemViewWithOffset:(CGFloat)offset
-{
-    //set up base transform
-    CATransform3D transform = CATransform3DIdentity;
-    transform.m34 = _perspective;
-    transform = CATransform3DTranslate(transform, -_viewpointOffset.width, -_viewpointOffset.height, 0.0);
-    
-    //perform transforms
-    CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
-    if (_vertical) {
-        return CATransform3DTranslate(transform, 0.0, offset * _itemWidth * spacing, 0.0);
-    } else {
-        return CATransform3DTranslate(transform, offset * _itemWidth * spacing, 0.0, 0.0);
-    }
-}
-
-- (void)transformItemView:(UIView *)view atIndex:(NSInteger)index
-{
-    //calculate offset
-    CGFloat offset = [self offsetForItemAtIndex:index];
-    
-    //update alpha
-    view.superview.layer.opacity = [self alphaForItemWithOffset:offset];
-    
-    //center view
-    view.superview.center = CGPointMake(self.bounds.size.width/2.0 + _contentOffset.width,
-                                        self.bounds.size.height/2.0 + _contentOffset.height);
-    
-    //enable/disable interaction
-    view.superview.userInteractionEnabled = (!_centerItemWhenSelected || index == self.currentItemIndex);
-    view.superview.layer.rasterizationScale = [UIScreen mainScreen].scale;
-    
-    [view layoutIfNeeded];
-    
-    CGFloat clampedOffset = MAX(-1.0, MIN(1.0, offset));
-    if (_decelerating || (_scrolling && !_dragging && !_didDrag) || (_autoscroll && !_dragging) ||
-        (!_wrapEnabled && (_scrollOffset < 0 || _scrollOffset >= _numberOfItems - 1))) {
-        if (offset > 0) {
-            _toggle = (offset <= 0.5)? -clampedOffset: (1.0 - clampedOffset);
-        } else {
-            _toggle = (offset > -0.5)? -clampedOffset: (- 1.0 - clampedOffset);
-        }
-    }
-    
-    //calculate transform
-    CATransform3D transform = [self transformForItemViewWithOffset:offset];
-    
-    //transform view
-    view.superview.layer.transform = transform;
-    
-    //backface culling
-    BOOL showBackfaces = view.layer.doubleSided;
-    if (showBackfaces) {
-        showBackfaces = YES;
-    }
-    showBackfaces = !![self valueForOption:WXSliderNeighborOptionShowBackfaces withDefault:showBackfaces];
-    
-    view.superview.hidden = !(showBackfaces ?: (transform.m33 > 0.0));
-}
-
-NSComparisonResult sliderNeighorCompareViewDepth(UIView *view1, UIView *view2, WXSliderNeighborView *self)
-{
-    //compare depths
-    CATransform3D t1 = view1.superview.layer.transform;
-    CATransform3D t2 = view2.superview.layer.transform;
-    CGFloat z1 = t1.m13 + t1.m23 + t1.m33 + t1.m43;
-    CGFloat z2 = t2.m13 + t2.m23 + t2.m33 + t2.m43;
-    CGFloat difference = z1 - z2;
-    
-    //if depths are equal, compare distance from current view
-    if (difference == 0.0) {
-        CATransform3D t3 = [self currentItemView].superview.layer.transform;
-        if (self.vertical) {
-            CGFloat y1 = t1.m12 + t1.m22 + t1.m32 + t1.m42;
-            CGFloat y2 = t2.m12 + t2.m22 + t2.m32 + t2.m42;
-            CGFloat y3 = t3.m12 + t3.m22 + t3.m32 + t3.m42;
-            difference = fabs(y2 - y3) - fabs(y1 - y3);
-        } else {
-            CGFloat x1 = t1.m11 + t1.m21 + t1.m31 + t1.m41;
-            CGFloat x2 = t2.m11 + t2.m21 + t2.m31 + t2.m41;
-            CGFloat x3 = t3.m11 + t3.m21 + t3.m31 + t3.m41;
-            difference = fabs(x2 - x3) - fabs(x1 - x3);
-        }
-    }
-    return (difference < 0.0)? NSOrderedAscending: NSOrderedDescending;
-}
-
-- (void)depthSortViews
-{
-    for (UIView *view in [[_itemViews allValues] sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))sliderNeighorCompareViewDepth context:(__bridge void *)self])
-    {
-        [_contentView bringSubviewToFront:(UIView *__nonnull)view.superview];
-    }
-}
-
-- (void)layOutItemViews
-{
-    //bail out if not set up yet
-    if (!_dataSource || !_contentView) {
-        return;
-    }
-    
-    //update wrap
-    _wrapEnabled = YES;
-    _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
-    
-    //no placeholders on wrapped carousels
-    _numberOfPlaceholdersToShow = _wrapEnabled? 0: _numberOfPlaceholders;
-    
-    //set item width
-    [self updateItemWidth];
-    
-    //update number of visible items
-    [self updateNumberOfVisibleItems];
-    
-    //prevent false index changed event
-    _previousScrollOffset = self.scrollOffset;
-    
-    //update offset multiplier
-    _offsetMultiplier = 1.0;
-    _offsetMultiplier = [self valueForOption:WXSliderNeighborOptionOffsetMultiplier withDefault:_offsetMultiplier];
-    
-    //align
-    if (!_scrolling && !_decelerating && !_autoscroll) {
-        if (_scrollToItemBoundary && self.currentItemIndex != -1) {
-            [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
-        } else {
-            _scrollOffset = [self clampedOffset:_scrollOffset];
-        }
-    }
-    //update views
-    if (_inited) {
-        [self didScroll];
-    }
-    
-    _inited = YES;
-}
-
-- (CGFloat)valueForOption:(WXSliderNeighborOption)option withDefault:(CGFloat)value
-{
-    if (_delegate && [_delegate respondsToSelector:@selector(sliderNeighbor:valueForOption:withDefault:)]) {
-        return [_delegate sliderNeighbor:self valueForOption:option withDefault:value];
-    }
-    return value;
-}
-
-#pragma mark -
-#pragma mark Gestures and taps
-
-- (NSInteger)viewOrSuperviewIndex:(UIView *)view
-{
-    if (view == nil || view == _contentView) {
-        return NSNotFound;
-    }
-    NSInteger index = [self indexOfItemView:view];
-    if (index == NSNotFound) {
-        return [self viewOrSuperviewIndex:view.superview];
-    }
-    return index;
-}
-
-- (BOOL)viewOrSuperview:(UIView *)view implementsSelector:(SEL)selector
-{
-    if (!view || view == self.contentView) {
-        return NO;
-    }
-    
-    Class viewClass = [view class];
-    while (viewClass && viewClass != [UIView class])
-    {
-        unsigned int numberOfMethods;
-        Method *methods = class_copyMethodList(viewClass, &numberOfMethods);
-        for (unsigned int i = 0; i < numberOfMethods; i++)
-        {
-            if (method_getName(methods[i]) == selector) {
-                free(methods);
-                return YES;
-            }
-        }
-        if (methods) free(methods);
-        viewClass = [viewClass superclass];
-    }
-    
-    return [self viewOrSuperview:view.superview implementsSelector:selector];
-}
-
-- (id)viewOrSuperview:(UIView *)view ofClass:(Class)class
-{
-    if (!view || view == self.contentView) {
-        return nil;
-    } else if ([view isKindOfClass:class]) {
-        return view;
-    }
-    return [self viewOrSuperview:view.superview ofClass:class];
-}
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch
-{
-    if (_scrollEnabled) {
-        _dragging = NO;
-        _scrolling = NO;
-        _decelerating = NO;
-    }
-    
-    if ([gesture isKindOfClass:[UITapGestureRecognizer class]]) {
-        //handle tap
-        NSInteger index = [self viewOrSuperviewIndex:touch.view];
-        if (index == NSNotFound && _centerItemWhenSelected) {
-            //view is a container view
-            index = [self viewOrSuperviewIndex:[touch.view.subviews lastObject]];
-        }
-        if (index != NSNotFound) {
-            if ([self viewOrSuperview:touch.view implementsSelector:@selector(touchesBegan:withEvent:)]) {
-                return NO;
-            }
-        }
-    } else if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
-        if (!_scrollEnabled) {
-            return NO;
-        } else if ([self viewOrSuperview:touch.view implementsSelector:@selector(touchesMoved:withEvent:)]) {
-            UIScrollView *scrollView = [self viewOrSuperview:touch.view ofClass:[UIScrollView class]];
-            if (scrollView) {
-                return !scrollView.scrollEnabled ||
-                (self.vertical && scrollView.contentSize.height <= scrollView.frame.size.height) ||
-                (!self.vertical && scrollView.contentSize.width <= scrollView.frame.size.width);
-            }
-            if ([self viewOrSuperview:touch.view ofClass:[UIButton class]] ||
-                [self viewOrSuperview:touch.view ofClass:[UIBarButtonItem class]]) {
-                return YES;
-            }
-            return NO;
-        }
-    }
-    return YES;
-}
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
-   //if the view which the otherGestureRecognizer works on is a scrollview and also it is scrollEnabled vertically ,at this time,we should not block the guesture from being recognized by the otherGestureRecognize
-    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
-        if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]]) {
-            UIScrollView* scrollview = (UIScrollView *)otherGestureRecognizer.view;
-            if (scrollview.scrollEnabled) {
-                UIPanGestureRecognizer* panRcgn= (UIPanGestureRecognizer *)gestureRecognizer;
-                //check offset for confirming vertival movement
-                if (fabs([panRcgn translationInView:panRcgn.view].y) > fabs([panRcgn translationInView:panRcgn.view].x)*16) {
-                    return YES;
-                }
-            }
-        }
-    }
-    return NO;
-}
-
-
-- (void)didPan:(UIPanGestureRecognizer *)panGesture
-{
-    if (_scrollEnabled && _numberOfItems) {
-        switch (panGesture.state)
-        {
-            case UIGestureRecognizerStateBegan:
-            {
-                _dragging = YES;
-                _scrolling = NO;
-                _decelerating = NO;
-                _previousTranslation = _vertical? [panGesture translationInView:self].y: [panGesture translationInView:self].x;
-                
-                [_delegate sliderNeighborWillBeginDragging:self];
-                break;
-            }
-            case UIGestureRecognizerStateEnded:
-            case UIGestureRecognizerStateCancelled:
-            case UIGestureRecognizerStateFailed:
-            {
-                _dragging = NO;
-                _didDrag = YES;
-                if ([self shouldDecelerate]) {
-                    _didDrag = NO;
-                    [self startDecelerating];
-                }
-                
-                [self pushAnimationState:YES];
-                [_delegate sliderNeighborDidEndDragging:self willDecelerate:_decelerating];
-                [self popAnimationState];
-                
-                if (!_decelerating) {
-                    if ((_scrollToItemBoundary || fabs(_scrollOffset - [self clampedOffset:_scrollOffset]) > FLOAT_ERROR_MARGIN) && !_autoscroll) {
-                        if (fabs(_scrollOffset - self.currentItemIndex) < FLOAT_ERROR_MARGIN) {
-                            //call scroll to trigger events for legacy support reasons
-                            //even though technically we don't need to scroll at all
-                            [self scroll2ItemViewAtIndex:self.currentItemIndex duration:0.01];
-                        } else {
-                            [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
-                        }
-                    } else {
-                        [self depthSortViews];
-                    }
-                } else {
-                    [self pushAnimationState:YES];
-                    [_delegate sliderNeighborWillBeginDecelerating:self];
-                    [self popAnimationState];
-                }
-                break;
-            }
-            case UIGestureRecognizerStateChanged:
-            {
-                CGFloat translation = _vertical? [panGesture translationInView:self].y: [panGesture translationInView:self].x;
-                CGFloat velocity = _vertical? [panGesture velocityInView:self].y: [panGesture velocityInView:self].x;
-                
-                CGFloat factor = 1.0;
-                if (!_wrapEnabled && _bounces) {
-                    factor = 1.0 - MIN(fabs(_scrollOffset - [self clampedOffset:_scrollOffset]),
-                                       _bounceDistance) / _bounceDistance;
-                }
-                
-                _startVelocity = -velocity * factor * _scrollSpeed / _itemWidth;
-                _scrollOffset -= (translation - _previousTranslation) * factor * _offsetMultiplier / _itemWidth;
-                _previousTranslation = translation;
-                [self didScroll];
-                break;
-            }
-            case UIGestureRecognizerStatePossible:
-            {
-                //do nothing
-                break;
-            }
-        }
-    }
-}
-
-- (void)didTap:(UITapGestureRecognizer *)tapGesture
-{
-    //check for tapped view
-    NSInteger index = [self indexOfItemView:[self itemViewAtPoint:[tapGesture locationInView:_contentView]]];
-    if (index != NSNotFound) {
-        if (!_delegate || [_delegate sliderNeighbor:self shouldSelectItemAtIndex:index]) {
-            if ((index != self.currentItemIndex && _centerItemWhenSelected) ||
-                (index == self.currentItemIndex && _scrollToItemBoundary)) {
-                [self scroll2ItemViewAtIndex:index animated:YES];
-            }
-            [_delegate sliderNeighbor:self didSelectItemAtIndex:index];
-        } else if (_scrollEnabled && _scrollToItemBoundary && _autoscroll) {
-            [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
-        }
-    } else {
-        [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
-    }
-}
-
-- (void)pushAnimationState:(BOOL)enabled
-{
-    [CATransaction begin];
-    [CATransaction setDisableActions:!enabled];
-}
-
-- (void)popAnimationState
-{
-    [CATransaction commit];
-}
-
-- (void)reloadData
-{
-    //remove old views
-    for (UIView *view in [_itemViews allValues])
-    {
-        [view.superview removeFromSuperview];
-    }
-    
-    //bail out if not set up yet
-    if (!_dataSource || !_contentView)
-    {
-        return;
-    }
-    
-    //get number of items and placeholders
-    _numberOfVisibleItems = 0;
-    _numberOfItems = [_dataSource numberOfItemsInSliderNeighbor:self];
-    if ([_dataSource respondsToSelector:@selector(numberOfPlaceholdersInsliderNeighbor:)]) {
-        _numberOfPlaceholders = [_dataSource numberOfPlaceholdersInsliderNeighbor:self];
-    }
-    
-    //reset view pools
-    self.itemViews = [NSMutableDictionary dictionary];
-    self.itemViewPool = [NSMutableSet set];
-    self.placeholderViewPool = [NSMutableSet setWithCapacity:_numberOfPlaceholders];
-    
-    //layout views
-    [self setNeedsLayout];
-    
-    //fix scroll offset
-    if (_numberOfItems > 0 && _scrollOffset < 0.0)
-    {
-        [self scroll2ItemViewAtIndex:0 animated:(_numberOfPlaceholders > 0)];
-    }
-}
-
-#pragma mark scrolling
-
-- (NSInteger)currentItemIndex
-{
-    return [self clampedIndex:round(_scrollOffset)];
-}
-
-- (NSInteger)nextItemIndex
-{
-    return ([self currentItemIndex]+1)%_numberOfItems;
-}
-
-- (NSInteger)lastItemIndex
-{
-    if ([self currentItemIndex] == 0) {
-        return _numberOfItems - 1;
-    }
-    return ((NSInteger)round(abs((int)[self currentItemIndex] - 1))) % _numberOfItems;
-}
-
-- (NSInteger)minScrollDistanceFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex
-{
-    NSInteger directDistance = toIndex - fromIndex;
-    if (_wrapEnabled)
-    {
-        NSInteger wrappedDistance = MIN(toIndex, fromIndex) + _numberOfItems - MAX(toIndex, fromIndex);
-        if (fromIndex < toIndex)
-        {
-            wrappedDistance = -wrappedDistance;
-        }
-        return (ABS(directDistance) <= ABS(wrappedDistance))? directDistance: wrappedDistance;
-    }
-    return directDistance;
-}
-
-- (NSInteger)clampedIndex:(NSInteger)index
-{
-    if (_numberOfItems == 0)
-    {
-        return -1;
-    } else if (_wrapEnabled) {
-        return index - floor((CGFloat)index / (CGFloat)_numberOfItems) * _numberOfItems;
-    } else {
-        return MIN(MAX(0, index), MAX(0, _numberOfItems - 1));
-    }
-}
-
-- (CGFloat)clampedOffset:(CGFloat)offset
-{
-    if (_numberOfItems == 0) {
-        return -1.0;
-    } else if (_wrapEnabled) {
-        return offset - floor(offset / (CGFloat)_numberOfItems) * _numberOfItems;
-    } else {
-        return MIN(MAX(0.0, offset), MAX(0.0, (CGFloat)_numberOfItems - 1.0));
-    }
-}
-
-- (CGFloat)minScrollDistanceFromOffset:(CGFloat)fromOffset toOffset:(CGFloat)toOffset
-{
-    CGFloat directDistance = toOffset - fromOffset;
-    if (_wrapEnabled) {
-        CGFloat wrappedDistance = MIN(toOffset, fromOffset) + _numberOfItems - MAX(toOffset, fromOffset);
-        if (fromOffset < toOffset) {
-            wrappedDistance = -wrappedDistance;
-        }
-        return (fabs(directDistance) <= fabs(wrappedDistance))? directDistance: wrappedDistance;
-    }
-    return directDistance;
-}
-
-- (void)scrollByOffset:(CGFloat)offset duration:(NSTimeInterval)duration
-{
-    if (duration > 0.0) {
-        _decelerating = NO;
-        _scrolling = YES;
-        _startTime = CACurrentMediaTime();
-        _startOffset = _scrollOffset;
-        _scrollDuration = duration;
-        _endOffset = _startOffset + offset;
-        if (!_wrapEnabled) {
-            _endOffset = [self clampedOffset:_endOffset];
-        }
-        [_delegate sliderNeighborWillBeginScrollingAnimation:self];
-        [self startAnimation];
-    } else {
-        self.scrollOffset += offset;
-    }
-}
-
-- (void)scroll2Offset:(CGFloat)offset duration:(NSTimeInterval)duration
-{
-    [self scrollByOffset:[self minScrollDistanceFromOffset:_scrollOffset toOffset:offset] duration:duration];
-    if (_delegate && [_delegate respondsToSelector:@selector(sliderNeighbor:didScrollToItemAtIndex:)]) {
-        [_delegate sliderNeighbor:self didScrollToItemAtIndex:offset];
-    }
-}
-
-- (void)scroll2ItemViewAtIndex:(NSInteger)index duration:(NSTimeInterval)duration
-{
-    [self scroll2Offset:index duration:duration];
-}
-
-- (void)scroll2ItemViewAtIndex:(NSInteger)index animated:(BOOL)animated
-{
-    [self scroll2ItemViewAtIndex:index duration:animated? 0.6: 0];
-}
-
-- (void)scrollByNumberOfItems:(NSInteger)itemCount duration:(NSTimeInterval)duration
-{
-    if (duration > 0.0) {
-        CGFloat offset = 0.0;
-        if (itemCount > 0) {
-            offset = (floor(_scrollOffset) + itemCount) - _scrollOffset;
-        } else if (itemCount < 0) {
-            offset = (ceil(_scrollOffset) + itemCount) - _scrollOffset;
-        } else {
-            offset = round(_scrollOffset) - _scrollOffset;
-        }
-        [self scrollByOffset:offset duration:duration];
-    } else {
-        self.scrollOffset = [self clampedIndex:_previousItemIndex + itemCount];
-    }
-}
-
-- (void)removeItemAtIndex:(NSInteger)index animated:(BOOL)animated
-{
-    index = [self clampedIndex:index];
-    UIView *itemView = [self itemViewAtIndex:index];
-    
-    if (animated) {
-        [UIView beginAnimations:nil context:nil];
-        [UIView setAnimationDuration:0.1];
-        [UIView setAnimationDelegate:itemView.superview];
-        [UIView setAnimationDidStopSelector:@selector(removeFromSuperview)];
-        [self performSelector:@selector(queueItemView:) withObject:itemView afterDelay:0.1];
-        itemView.superview.layer.opacity = 0.0;
-        [UIView commitAnimations];
-        
-        [UIView beginAnimations:nil context:nil];
-        [UIView setAnimationDelay:0.1];
-        [UIView setAnimationDuration:INSERT_DURATION];
-        [UIView setAnimationDelegate:self];
-        [UIView setAnimationDidStopSelector:@selector(depthSortViews)];
-        [self removeViewAtIndex:index];
-        _numberOfItems --;
-        _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
-        [self updateNumberOfVisibleItems];
-        _scrollOffset = self.currentItemIndex;
-        [self didScroll];
-        [UIView commitAnimations];
-    } else {
-        [self pushAnimationState:NO];
-        [self queueItemView:itemView];
-        [itemView.superview removeFromSuperview];
-        [self removeViewAtIndex:index];
-        _numberOfItems --;
-        _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
-        _scrollOffset = self.currentItemIndex;
-        [self didScroll];
-        [self depthSortViews];
-        [self popAnimationState];
-    }
-}
-
-- (void)insertItemAtIndex:(NSInteger)index animated:(BOOL)animated
-{
-    _numberOfItems ++;
-    _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
-    [self updateNumberOfVisibleItems];
-    
-    index = [self clampedIndex:index];
-    [self insertView:nil atIndex:index];
-    [self loadViewAtIndex:index];
-    
-    if (fabs(_itemWidth) < FLOAT_ERROR_MARGIN) {
-        [self updateItemWidth];
-    }
-    
-    if (animated) {
-        [UIView beginAnimations:nil context:nil];
-        [UIView setAnimationDuration:INSERT_DURATION];
-        [UIView setAnimationDelegate:self];
-        [UIView setAnimationDidStopSelector:@selector(didScroll)];
-        [self transformItemViews];
-        [UIView commitAnimations];
-    } else {
-        [self pushAnimationState:NO];
-        [self didScroll];
-        [self popAnimationState];
-    }
-    
-    if (_scrollOffset < 0.0) {
-        [self scroll2ItemViewAtIndex:0 animated:(animated && _numberOfPlaceholders)];
-    }
-}
-
-- (void)reloadItemAtIndex:(NSInteger)index animated:(BOOL)animated
-{
-    //get container view
-    UIView *containerView = [[self itemViewAtIndex:index] superview];
-    if (containerView) {
-        if (animated) {
-            //fade transition
-            CATransition *transition = [CATransition animation];
-            transition.duration = INSERT_DURATION;
-            transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
-            transition.type = kCATransitionFade;
-            [containerView.layer addAnimation:transition forKey:nil];
-        }
-        
-        //reload view
-        [self loadViewAtIndex:index withContainerView:containerView];
-    }
-}
-
-#pragma mark Animation
-- (void)step
-{
-    [self pushAnimationState:NO];
-    NSTimeInterval currentTime = CACurrentMediaTime();
-    double delta = currentTime - _lastTime;
-    _lastTime = currentTime;
-    
-    if (_scrolling && !_dragging) {
-        NSTimeInterval time = MIN(1.0, (currentTime - _startTime) / _scrollDuration);
-        delta = [self easeInOut:time];
-        _scrollOffset = _startOffset + (_endOffset - _startOffset) * delta;
-        [self didScroll];
-        if (time >= 1.0) {
-            _scrolling = NO;
-            [self depthSortViews];
-            [self pushAnimationState:YES];
-            [_delegate sliderNeighborDidEndScrollingAnimation:self];
-            [self popAnimationState];
-        }
-    } else if (_decelerating) {
-        CGFloat time = MIN(_scrollDuration, currentTime - _startTime);
-        CGFloat acceleration = -_startVelocity/_scrollDuration;
-        CGFloat distance = _startVelocity * time + 0.5 * acceleration * pow(time, 2.0);
-        _scrollOffset = _startOffset + distance;
-        [self didScroll];
-        if (fabs(time - _scrollDuration) < FLOAT_ERROR_MARGIN) {
-            _decelerating = NO;
-            [self pushAnimationState:YES];
-            [_delegate sliderNeighborDidEndDecelerating:self];
-            [self popAnimationState];
-            if ((_scrollToItemBoundary || fabs(_scrollOffset - [self clampedOffset:_scrollOffset]) > FLOAT_ERROR_MARGIN) && !_autoscroll) {
-                if (fabs(_scrollOffset - self.currentItemIndex) < FLOAT_ERROR_MARGIN) {
-                    //call scroll to trigger events for legacy support reasons
-                    //even though technically we don't need to scroll at all
-                    [self scroll2ItemViewAtIndex:self.currentItemIndex duration:0.01];
-                } else {
-                    [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
-                }
-            } else {
-                CGFloat difference = round(_scrollOffset) - _scrollOffset;
-                if (difference > 0.5) {
-                    difference = difference - 1.0;
-                } else if (difference < -0.5) {
-                    difference = 1.0 + difference;
-                }
-                _toggleTime = currentTime - MAX_TOGGLE_DURATION * fabs(difference);
-                _toggle = MAX(-1.0, MIN(1.0, -difference));
-            }
-        }
-    } else if (_autoscroll && !_dragging) {
-        //autoscroll goes backwards from what you'd expect, for historical reasons
-        self.scrollOffset = [self clampedOffset:_scrollOffset - delta * _autoscroll];
-    } else if (fabs(_toggle) > FLOAT_ERROR_MARGIN) {
-        NSTimeInterval toggleDuration = _startVelocity? MIN(1.0, MAX(0.0, 1.0 / fabs(_startVelocity))): 1.0;
-        toggleDuration = MIN_TOGGLE_DURATION + (MAX_TOGGLE_DURATION - MIN_TOGGLE_DURATION) * toggleDuration;
-        NSTimeInterval time = MIN(1.0, (currentTime - _toggleTime) / toggleDuration);
-        delta = [self easeInOut:time];
-        _toggle = (_toggle < 0.0)? (delta - 1.0): (1.0 - delta);
-        [self didScroll];
-    } else if (!_autoscroll) {
-        [self stopAnimation];
-    }
-    
-    [self popAnimationState];
-}
-
-- (CGFloat)easeInOut:(CGFloat)time
-{
-    return (time < 0.5)? 0.5 * pow(time * 2.0, 3.0): 0.5 * pow(time * 2.0 - 2.0, 3.0) + 1.0;
-}
-
-- (CGFloat)decelerationDistance
-{
-    CGFloat acceleration = -_startVelocity * DECELERATION_MULTIPLIER * (1.0 - _decelerationRate);
-    return -pow(_startVelocity, 2.0) / (2.0 * acceleration);
-}
-
-- (void)startDecelerating
-{
-    CGFloat distance = [self decelerationDistance];
-    _startOffset = _scrollOffset;
-    _endOffset = _startOffset + distance;
-    if (_pagingEnabled) {
-        if (distance > 0.0) {
-            _endOffset = ceil(_startOffset);
-        } else {
-            _endOffset = floor(_startOffset);
-        }
-    } else if (_stopAtItemBoundary) {
-        if (distance > 0.0) {
-            _endOffset = ceil(_endOffset);
-        } else {
-            _endOffset = floor(_endOffset);
-        }
-    }
-    if (!_wrapEnabled) {
-        if (_bounces) {
-            _endOffset = MAX(-_bounceDistance, MIN(_numberOfItems - 1.0 + _bounceDistance, _endOffset));
-        } else {
-            _endOffset = [self clampedOffset:_endOffset];
-        }
-    }
-    distance = _endOffset - _startOffset;
-    
-    _startTime = CACurrentMediaTime();
-    _scrollDuration = fabs(distance) / fabs(0.5 * _startVelocity);
-    
-    if (distance != 0.0) {
-        _decelerating = YES;
-        [self startAnimation];
-    }
-}
-
-
-- (BOOL)shouldDecelerate
-{
-    return (fabs(_startVelocity) > SCROLL_SPEED_THRESHOLD) &&
-    (fabs([self decelerationDistance]) > DECELERATE_THRESHOLD);
-}
-
-- (BOOL)shouldScroll
-{
-    return (fabs(_startVelocity) > SCROLL_SPEED_THRESHOLD) &&
-    (fabs(_scrollOffset - self.currentItemIndex) > SCROLL_DISTANCE_THRESHOLD);
-}
-
-- (void)didScroll
-{
-    if (_wrapEnabled || !_bounces) {
-        _scrollOffset = [self clampedOffset:_scrollOffset];
-    } else {
-        CGFloat min = -_bounceDistance;
-        CGFloat max = MAX(_numberOfItems - 1, 0.0) + _bounceDistance;
-        if (_scrollOffset < min) {
-            _scrollOffset = min;
-            _startVelocity = 0.0;
-        } else if (_scrollOffset > max) {
-            _scrollOffset = max;
-            _startVelocity = 0.0;
-        }
-    }
-    
-    //check if index has changed
-    NSInteger difference = [self minScrollDistanceFromIndex:self.currentItemIndex toIndex:self.previousItemIndex];
-    if (difference) {
-        _toggleTime = CACurrentMediaTime();
-        _toggle = MAX(-1, MIN(1, difference));
-        
-        [self startAnimation];
-    }
-    
-    [self loadUnloadViews];
-    [self transformItemViews];
-    
-    //notify delegate of offset change
-    if (fabs(_scrollOffset - _previousScrollOffset) > 0.000001)
-    {
-        [self pushAnimationState:YES];
-        [_delegate sliderNeighborDidScroll:self];
-        [self popAnimationState];
-    }
-    
-    //notify delegate of index change
-    if (_previousItemIndex != self.currentItemIndex)
-    {
-        [self pushAnimationState:YES];
-        if([_delegate respondsToSelector:@selector(sliderNeighborCurrentItemIndexDidChange:from:to:)]) {
-            [_delegate sliderNeighborCurrentItemIndexDidChange:self from:_previousItemIndex to:self.currentItemIndex];
-        }
-        [self popAnimationState];
-    }
-    
-    //update previous index
-    _previousScrollOffset = _scrollOffset;
-    _previousItemIndex = self.currentItemIndex;
-    [self.indicator setCurrentPoint:self.currentItemIndex];
-}
-
-- (void)startAnimation
-{
-    if (!_timer) {
-        self.timer = [NSTimer timerWithTimeInterval:1.0/60.0
-                                             target:self
-                                           selector:@selector(step)
-                                           userInfo:nil
-                                            repeats:YES];
-        
-        [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
-        [[NSRunLoop mainRunLoop] addTimer:_timer forMode:UITrackingRunLoopMode];
-        
-    }
-}
-
-- (void)stopAnimation
-{
-    [_timer invalidate];
-    _timer = nil;
-}
-
-#pragma mark -
-#pragma mark view management
-
-- (NSArray *)indexesForVisibleItems
-{
-    return [[_itemViews allKeys] sortedArrayUsingSelector:@selector(compare:)];
-}
-
-- (NSArray *)visibleItemViews
-{
-    NSArray *indexes = [self indexesForVisibleItems];
-    return [_itemViews objectsForKeys:indexes notFoundMarker:[NSNull null]];
-}
-
-- (UIView *)itemViewAtIndex:(NSInteger)index
-{
-    return _itemViews[@(index)];
-}
-
-- (UIView *)currentItemView
-{
-    return [self itemViewAtIndex:self.currentItemIndex];
-}
-
-- (NSInteger)indexOfItemView:(UIView *)view
-{
-    NSInteger index = [[_itemViews allValues] indexOfObject:view];
-    if (index != NSNotFound) {
-        return [[_itemViews allKeys][index] integerValue];
-    }
-    return NSNotFound;
-}
-
-- (NSInteger)indexOfItemViewOrSubview:(UIView *)view
-{
-    NSInteger index = [self indexOfItemView:view];
-    if (index == NSNotFound && view != nil && view != _contentView) {
-        return [self indexOfItemViewOrSubview:view.superview];
-    }
-    return index;
-}
-
-- (UIView *)itemViewAtPoint:(CGPoint)point
-{
-    for (UIView *view in [[[_itemViews allValues] sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))sliderNeighorCompareViewDepth context:(__bridge void *)self] reverseObjectEnumerator])
-    {
-        if ([view.superview.layer hitTest:point]) {
-            return view;
-        }
-    }
-    return nil;
-}
-
-- (void)setItemView:(UIView *)view forIndex:(NSInteger)index
-{
-    _itemViews[@(index)] = view;
-}
-
-- (void)removeViewAtIndex:(NSInteger)index
-{
-    NSMutableDictionary *newItemViews = [NSMutableDictionary dictionaryWithCapacity:[_itemViews count] - 1];
-    for (NSNumber *number in [self indexesForVisibleItems])
-    {
-        NSInteger i = [number integerValue];
-        if (i < index) {
-            newItemViews[number] = _itemViews[number];
-        } else if (i > index) {
-            newItemViews[@(i - 1)] = _itemViews[number];
-        }
-    }
-    self.itemViews = newItemViews;
-    [self.indicator setPointCount:self.itemViews.count];
-}
-
-- (void)insertView:(UIView *)view atIndex:(NSInteger)index
-{
-    NSMutableDictionary *newItemViews = [NSMutableDictionary dictionaryWithCapacity:[_itemViews count] + 1];
-    for (NSNumber *number in [self indexesForVisibleItems])
-    {
-        NSInteger i = [number integerValue];
-        if (i < index) {
-            newItemViews[number] = _itemViews[number];
-        } else {
-            newItemViews[@(i + 1)] = _itemViews[number];
-        }
-    }
-    if (view) {
-        [self setItemView:view forIndex:index];
-    }
-    self.itemViews = newItemViews;
-    
-    [self.indicator setPointCount:self.itemViews.count];
-}
-
-#pragma mark -
-#pragma mark View loading
-
-- (UIView *)loadViewAtIndex:(NSInteger)index withContainerView:(UIView *)containerView
-{
-    [self pushAnimationState:NO];
-    
-    UIView *view = nil;
-    if (index < 0) {
-        view = [_dataSource sliderNeighbor:self placeholderViewAtIndex:(NSInteger)(ceil((CGFloat)_numberOfPlaceholdersToShow/2.0)) + index reusingView:[self dequeuePlaceholderView]];
-    } else if (index >= _numberOfItems) {
-        view = [_dataSource sliderNeighbor:self placeholderViewAtIndex:_numberOfPlaceholdersToShow/2.0 + index - _numberOfItems reusingView:[self dequeuePlaceholderView]];
-    } else {
-        view = [_dataSource sliderNeighbor:self viewForItemAtIndex:index reusingView:[self dequeueItemView]];
-    }
-    
-    if (view == nil) {
-        view = [[UIView alloc] init];
-    }
-    
-    [self setItemView:view forIndex:index];
-    if (containerView) {
-        //get old item view
-        UIView *oldItemView = [containerView.subviews lastObject];
-        if (index < 0 || index >= _numberOfItems) {
-            [self queuePlaceholderView:oldItemView];
-        } else {
-            [self queueItemView:oldItemView];
-        }
-        
-        //set container frame
-        CGRect frame = containerView.bounds;
-        if(_vertical) {
-            frame.size.width = view.frame.size.width;
-            frame.size.height = MIN(_itemWidth, view.frame.size.height);
-        } else {
-            frame.size.width = MIN(_itemWidth, view.frame.size.width);
-            frame.size.height = view.frame.size.height;
-        }
-        containerView.bounds = frame;
-        
-        //set view frame
-        frame = view.frame;
-        frame.origin.x = (containerView.bounds.size.width - frame.size.width) / 2.0;
-        frame.origin.y = (containerView.bounds.size.height - frame.size.height) / 2.0;
-        view.frame = frame;
-        
-        //switch views
-        [oldItemView removeFromSuperview];
-        [containerView addSubview:view];
-    } else {
-        [_contentView addSubview:[self containView:view]];
-    }
-    view.superview.layer.opacity = 0.0;
-    [self transformItemView:view atIndex:index];
-    
-    [self popAnimationState];
-    
-    return view;
-}
-
-- (UIView *)loadViewAtIndex:(NSInteger)index
-{
-    return [self loadViewAtIndex:index withContainerView:nil];
-}
-
-- (void)loadUnloadViews
-{
-    //set item width
-    [self updateItemWidth];
-    
-    //update number of visible items
-    [self updateNumberOfVisibleItems];
-    
-    //calculate visible view indices
-    NSMutableSet *visibleIndices = [NSMutableSet setWithCapacity:_numberOfVisibleItems];
-    NSInteger min = -(NSInteger)(ceil((CGFloat)_numberOfPlaceholdersToShow/2.0));
-    NSInteger max = _numberOfItems - 1 + _numberOfPlaceholdersToShow/2;
-    NSInteger offset = self.currentItemIndex - _numberOfVisibleItems/2;
-    if (!_wrapEnabled) {
-        offset = MAX(min, MIN(max - _numberOfVisibleItems + 1, offset));
-    }
-    for (NSInteger i = 0; i < _numberOfVisibleItems; i++)
-    {
-        NSInteger index = i + offset;
-        if (_wrapEnabled) {
-            index = [self clampedIndex:index];
-        }
-        CGFloat alpha = [self alphaForItemWithOffset:[self offsetForItemAtIndex:index]];
-        if (alpha) {
-            //only add views with alpha > 0
-            [visibleIndices addObject:@(index)];
-        }
-    }
-    
-    //remove offscreen views
-    for (NSNumber *number in [_itemViews allKeys])
-    {
-        if (![visibleIndices containsObject:number]) {
-            UIView *view = _itemViews[number];
-            if ([number integerValue] < 0 || [number integerValue] >= _numberOfItems) {
-                [self queuePlaceholderView:view];
-            } else {
-                [self queueItemView:view];
-            }
-            [view.superview removeFromSuperview];
-            [(NSMutableDictionary *)_itemViews removeObjectForKey:number];
-        }
-    }
-    
-    //add onscreen views
-    for (NSNumber *number in visibleIndices)
-    {
-        UIView *view = _itemViews[number];
-        if (view == nil) {
-            [self loadViewAtIndex:[number integerValue]];
-        }
-    }
-}
-
-#pragma mark View queing
-
-- (void)queueItemView:(UIView *)view
-{
-    if (view) {
-        [_itemViewPool addObject:view];
-    }
-}
-
-- (void)queuePlaceholderView:(UIView *)view
-{
-    if (view) {
-        [_placeholderViewPool addObject:view];
-    }
-}
-
-- (UIView *)dequeueItemView
-{
-    UIView *view = [_itemViewPool anyObject];
-    if (view) {
-        [_itemViewPool removeObject:view];
-    }
-    return view;
-}
-
-- (UIView *)dequeuePlaceholderView
-{
-    UIView *view = [_placeholderViewPool anyObject];
-    if (view) {
-        [_placeholderViewPool removeObject:view];
-    }
-    return view;
-}
-
-- (void)setIndicator:(WXIndicatorView *)indicator
-{
-    _indicator = indicator;
-    [_indicator setPointCount:self.numberOfItems];
-    [_indicator setCurrentPoint:[self currentItemIndex]];
-}
-
-- (void)layoutSubviews
-{
-    [super layoutSubviews];
-    _contentView.frame = self.bounds;
-    [self layOutItemViews];
-}
-
-@end
-
-@implementation NSObject (WXSliderNeighborView)
-
-- (NSUInteger)numberOfPlaceholdersInSliderNeighborView:(__unused WXSliderNeighborView *)sliderNeighbor { return 0; }
-- (void)sliderNeighborWillBeginScrollingAnimation:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidEndScrollingAnimation:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidScroll:(__unused WXSliderNeighborView *)sliderNeighbor {}
-
-- (void)sliderNeighborCurrentItemIndexDidChange:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborWillBeginDragging:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidEndDragging:(__unused WXSliderNeighborView *)sliderNeighbor willDecelerate:(__unused BOOL)decelerate {}
-- (void)sliderNeighborWillBeginDecelerating:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidEndDecelerating:(__unused WXSliderNeighborView *)sliderNeighbor {}
-
-- (BOOL)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor shouldSelectItemAtIndex:(__unused NSInteger)index { return YES; }
-- (void)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor didSelectItemAtIndex:(__unused NSInteger)index {}
-
-- (CGFloat)sliderNeighborItemWidth:(__unused WXSliderNeighborView *)sliderNeighbor { return 0; }
-- (CATransform3D)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor
-   itemTransformForOffset:(__unused CGFloat)offset
-            baseTransform:(CATransform3D)transform { return transform; }
-- (CGFloat)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor
-     valueForOption:(__unused WXSliderNeighborOption)option
-        withDefault:(CGFloat)value { return value; }
-
-@end
-
-@interface WXSliderNeighborComponent () <WXIndicatorComponentDelegate>{
-    WXPixelType neighborSpace;
-    CGFloat neighborAlpha;
-    CGFloat neighborScale;
-    CGFloat currentItemScale;
-}
-
-@property (nonatomic, strong) WXSliderNeighborView *sliderView;
-@property (nonatomic, assign) BOOL  autoPlay;
-@property (nonatomic, assign) NSUInteger interval;
-@property (nonatomic, assign) NSInteger index;
-@property (nonatomic, strong) NSMutableArray *items;
-@property (nonatomic, strong) NSTimer *autoTimer;
-@property (nonatomic, assign) BOOL  sliderChangeEvent;
-@property (nonatomic, assign) NSInteger currentIndex;
-@property (nonatomic) CGRect itemRect;
-@property (nonatomic, assign) BOOL scrollable;
-
-@end
-
-#define DEFAULT_NEIGHBOR_ITEM_SCALE 0.8
-#define DEFAULT_CURRENT_ITEM_SCALE 0.9
-#define DEFAULT_NEIGHBOR_ALPHA 0.6
-#define DEFAULT_ANIMATION_DURATION 0.3
-#define DEFAULT_NEIGHBOR_SPACE 25
-
-
-@implementation WXSliderNeighborComponent
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance {
-    if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
-        _sliderChangeEvent = NO;
-        _interval = 3000;
-        _items = [NSMutableArray array];
-        _itemRect = CGRectNull;
-        self->neighborAlpha = DEFAULT_NEIGHBOR_ALPHA;
-        self->neighborScale = DEFAULT_NEIGHBOR_ITEM_SCALE;
-        self->currentItemScale = DEFAULT_CURRENT_ITEM_SCALE;
-        self->neighborSpace = [WXConvert WXPixelType:@(DEFAULT_NEIGHBOR_SPACE) scaleFactor:self.weexInstance.pixelScaleFactor];
-        _scrollable = YES;
-        [self setAttributes:attributes];
-    
-        _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
-    }
-    self.cssNode->style.flex_direction = CSS_FLEX_DIRECTION_ROW;
-    
-    return self;
-}
-
-
-#pragma mark life circle
-
-- (UIView *)loadView
-{
-    _sliderView = [[WXSliderNeighborView alloc] init];
-    return _sliderView;
-}
-
-- (void)dealloc
-{
-    _sliderView.delegate = nil;
-    _sliderView.dataSource = nil;
-    if (_autoPlay) {
-        [self _stopAutoPlayTimer];
-    }
-    _sliderView = nil;
-    [self.items removeAllObjects];
-}
-
-- (void)viewDidUnload
-{
-    [self.items removeAllObjects];
-}
-
-- (void)viewDidLoad
-{
-    _sliderView = (WXSliderNeighborView *)self.view;
-    _sliderView.delegate = self;
-    _sliderView.dataSource = self;
-    _sliderView.contentView.clipsToBounds = YES;
-    _sliderView.scrollEnabled = _scrollable;
-    UIAccessibilityTraits traits = UIAccessibilityTraitAdjustable;
-    
-    if (_autoPlay) {
-        traits |= UIAccessibilityTraitUpdatesFrequently;
-        [self _startAutoPlayTimer];
-    } else {
-        [self _stopAutoPlayTimer];
-    }
-    _sliderView.accessibilityTraits = traits;
-}
-
-- (void)layoutDidFinish {
-    
-    [_sliderView setCurrentItemIndex:_index];
-}
-
-- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
-    if (subcomponent->_positionType == WXPositionTypeFixed) {
-        [self.weexInstance.rootView addSubview:subcomponent.view];
-        return;
-    }
-    
-    // use _lazyCreateView to forbid component like cell's view creating
-    if(_lazyCreateView) {
-        subcomponent->_lazyCreateView = YES;
-    }
-    
-    if (!subcomponent->_lazyCreateView || (self->_lazyCreateView && [self isViewLoaded])) {
-        UIView *view = subcomponent.view;
-        
-        WXSliderNeighborView *sliderView = (WXSliderNeighborView *)self.view;
-        if ([view isKindOfClass:[WXIndicatorView class]]) {
-            ((WXIndicatorComponent *)subcomponent).delegate = self;
-            [sliderView addSubview:view];
-            return;
-        }
-        
-        if(index < 0) {
-            [self.items addObject:view];
-        } else {
-            [self.items insertObject:view atIndex:index];
-        }
-        
-        subcomponent.isViewFrameSyncWithCalculated = NO;
-        
-        if (index == -1) {
-            [sliderView insertView:view atIndex:index];
-        } else {
-            NSInteger offset = 0;
-            for (int i = 0; i < [self.items count]; ++i) {
-                if (index == i) break;
-                
-                if ([self.items[i] isKindOfClass:[WXIndicatorView class]]) {
-                    offset++;
-                }
-            }
-            [sliderView insertView:view atIndex:index - offset];
-        }
-        
-        [sliderView reloadData];
-    }
-}
-
-#pragma mark attributes update
-- (void)setAttributes:(NSDictionary *)attributes
-{
-    if (attributes[@"index"]) {
-        _index = [attributes[@"index"] integerValue];
-        _currentIndex = _index;
-    }
-    if (attributes[@"autoPlay"]) {
-        _autoPlay = [attributes[@"autoPlay"] boolValue];
-        if (_autoPlay) {
-            [self _startAutoPlayTimer];
-        } else {
-            [self _stopAutoPlayTimer];
-        }
-    }
-    if (attributes[@"interval"]) {
-        _interval = [attributes[@"interval"] integerValue];
-        
-        [self _stopAutoPlayTimer];
-        
-        if (_autoPlay) {
-            [self _startAutoPlayTimer];
-        }
-    }
-    if (attributes[@"neighborScale"]) {
-        [self setNeighborScale:attributes];
-    }
-    if (attributes[@"currentItemScale"]) {
-        [self setCurrentItemScale:attributes];
-    }
-    if (attributes[@"neighborAlpha"]) {
-        [self setNeighborAlpha:attributes];
-    }
-    if (attributes[@"neighborSpace"]) {
-        [self setNeighborSpace:attributes];
-    }
-    if (attributes[@"scrollable"]) {
-        _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
-        ((WXSliderNeighborView *)self.view).scrollEnabled = _scrollable;
-    }
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
-    [self setAttributes:attributes];
-    [self.sliderView setCurrentItemIndex:_index];
-    [self updateSliderPage:YES];
-}
-
-#pragma mark styles update
-- (void)updateStyles:(NSDictionary *)styles {
-    
-}
-
-#pragma mark event
-- (void)addEvent:(NSString *)eventName
-{
-    if ([eventName isEqualToString:@"change"]) {
-        _sliderChangeEvent = YES;
-    }
-}
-
-- (void)removeEvent:(NSString *)eventName
-{
-    if ([eventName isEqualToString:@"change"]) {
-        _sliderChangeEvent = NO;
-    }
-}
-
-#pragma mark WXIndicatorComponentDelegate Methods
-
-- (void)setIndicatorView:(WXIndicatorView *)indicatorView
-{
-    NSAssert(_sliderView, @"");
-    [_sliderView setIndicator:indicatorView];
-}
-
-- (void)setNeighborSpace:(NSDictionary *)attributes{
-    if(attributes[@"neighborSpace"]) {
-        self->neighborSpace = [WXConvert WXPixelType:attributes[@"neighborSpace"] scaleFactor:self.weexInstance.pixelScaleFactor];
-    }
-}
-
-- (void)setNeighborAlpha:(NSDictionary *)attributes {
-    if (attributes[@"neighborAlpha"]) {
-        self->neighborAlpha = [WXConvert CGFloat:attributes[@"neighborAlpha"]];
-        self->neighborAlpha = self->neighborAlpha >= 0 ? self->neighborAlpha : 0;
-        self->neighborAlpha = self->neighborAlpha <= 1 ? self->neighborAlpha: 1;
-    }
-}
-
-- (void)setCurrentItemScale:(NSDictionary *)attributes {
-    if (attributes[@"currentItemScale"]) {
-        self->currentItemScale = [WXConvert CGFloat:attributes[@"currentItemScale"]];
-        self->currentItemScale = self->currentItemScale >= 0 ? self->currentItemScale : 0;
-        self->currentItemScale = self->currentItemScale <= 1 ? self->currentItemScale: 1;
-    }
-}
-
-- (void)setNeighborScale:(NSDictionary *)attributes
-{
-    if (attributes[@"neighborScale"]) {
-        self->neighborScale = [WXConvert CGFloat:attributes[@"neighborScale"]];
-        self->neighborScale = self->neighborScale >= 0? self->neighborScale : 0;
-        self->neighborScale = self->neighborScale <= 1? self->neighborScale :1;
-    }
-}
-
-#pragma mark Private Methods
-
-- (void)_startAutoPlayTimer
-{
-    if (!self.autoTimer || ![self.autoTimer isValid]) {
-        __weak __typeof__(self) weakSelf = self;
-        self.autoTimer = [NSTimer wx_scheduledTimerWithTimeInterval:_interval/1000.0f block:^() {
-            [weakSelf _autoPlayOnTimer];
-        } repeats:YES];
-        [[NSRunLoop currentRunLoop] addTimer:self.autoTimer forMode:NSRunLoopCommonModes];
-    }
-}
-
-- (void)_stopAutoPlayTimer
-{
-    if (self.autoTimer && [self.autoTimer isValid]) {
-        [self.autoTimer invalidate];
-        self.autoTimer = nil;
-    }
-}
-
-- (void)_autoPlayOnTimer
-{
-    WXSliderNeighborView *sliderNeighborView = (WXSliderNeighborView *)self.view;
-    
-    int indicatorCnt = 0;
-    for (int i = 0; i < [self.items count]; ++i) {
-        if ([self.items[i] isKindOfClass:[WXIndicatorView class]]) {
-            indicatorCnt++;
-        }
-    }
-    
-    self.currentIndex ++;
-    if (self.currentIndex >= self.items.count - indicatorCnt) {
-        self.currentIndex = 0;
-    }
-    
-    [sliderNeighborView setCurrentItemIndex:self.currentIndex];
-}
-
-#pragma mark sliderNeighbor Delegate && dataSource
-
-- (NSInteger)numberOfItemsInSliderNeighbor:(WXSliderNeighborView *)sliderNeighbor {
-    return [self.items count];
-}
-
-- (NSInteger)numberOfPlaceholdersInsliderNeighbor:(WXSliderNeighborView *)sliderNeighbor {
-    return 2;
-}
-
-- (UIView *)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view {
-    
-    if (!view) {
-        if (index < [self.items count]) {
-            view = self.items[index];
-        }else {
-            return nil;
-        }
-    } else {
-        view.tag = 1;
-    }
-    
-    if (CGRectIsNull(_itemRect)) {
-        _itemRect = view.frame;
-    }
-    if (index == [self.items count] - 1) {
-        [self updateSliderPage:NO];
-    }
-    
-    return view;
-}
-
-- (BOOL)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor shouldSelectItemAtIndex:(NSInteger)index {
-    
-    return YES;
-}
-
-- (UIView *)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor placeholderViewAtIndex:(NSInteger)index reusingView:(UIView *)view
-{
-    
-    return view;
-}
-
-- (void)sliderNeighborDidEndScrollingAnimation:(WXSliderNeighborView *)sliderNeighbor
-{
-    [self updateSliderPage:YES];
-}
-
-- (void)sliderNeighbor:(WXSliderNeighborView *)sliderView didScrollToItemAtIndex:(NSInteger)index
-{
-    self.currentIndex = index;
-    
-    if (_sliderChangeEvent) {
-        [self fireEvent:@"change" params:@{@"index":@(index)} domChanges:@{@"attrs": @{@"index": @(index)}}];
-    }
-}
-
-- (void)sliderNeighborWillBeginDragging:(WXSliderNeighborView *)sliderNeighbor
-{
-    [self _stopAutoPlayTimer];
-}
-
-- (void)sliderNeighborDidEndDragging:(WXSliderNeighborView *)sliderNeighbor willDecelerate:(BOOL)decelerate {
-    if (_autoPlay) {
-        [self _startAutoPlayTimer];
-    }
-}
-
-- (void)updateSliderPage:(BOOL)animate
-{
-    __block UIView * currentView  = [self.sliderView itemViewAtIndex:[_sliderView currentItemIndex]];
-    __block UIView * lastView  = [self.sliderView itemViewAtIndex:[_sliderView lastItemIndex]];
-    __block UIView * nextView  = [self.sliderView itemViewAtIndex:[_sliderView nextItemIndex]];
-    
-    __block CGAffineTransform transfrom = CGAffineTransformIdentity;
-    float duration = 0;
-    __weak typeof(self) weakSelf = self;
-    if (animate) {
-        duration = DEFAULT_ANIMATION_DURATION;
-    }
-    
-    [UIView animateWithDuration:duration animations:^{
-        __strong typeof(self) strongSelf = weakSelf;
-        if (strongSelf) {
-            currentView.alpha = 1.0;
-            if (fabs(strongSelf->currentItemScale) > CGFLOAT_MIN) {
-                transfrom = CGAffineTransformConcat(transfrom,CGAffineTransformMakeScale(strongSelf->currentItemScale, strongSelf->currentItemScale));
-            }
-            currentView.transform = transfrom;
-            transfrom = CGAffineTransformIdentity;
-            if (fabs(strongSelf->neighborScale) <= CGFLOAT_MIN) {
-                strongSelf->neighborScale = DEFAULT_NEIGHBOR_ITEM_SCALE;
-            }
-            
-            CGFloat tx = 0.5*_itemRect.size.width*((1-self->neighborScale)+(1-self->currentItemScale))-self->neighborSpace;
-            transfrom = CGAffineTransformConcat(transfrom, CGAffineTransformMakeScale(strongSelf->neighborScale, strongSelf->neighborScale));
-            nextView.transform = CGAffineTransformConcat(transfrom,  CGAffineTransformMakeTranslation(-tx, 0));
-            lastView.transform = CGAffineTransformConcat(transfrom,  CGAffineTransformMakeTranslation(tx, 0));
-            lastView.alpha = strongSelf->neighborAlpha;
-            nextView.alpha = strongSelf->neighborAlpha;
-        }
-    }];
-}
-
-@end