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