| /* |
| * 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 "WXCycleSliderComponent.h" |
| #import "WXIndicatorComponent.h" |
| #import "WXComponent_internal.h" |
| #import "NSTimer+Weex.h" |
| #import "WXSDKManager.h" |
| #import "WXUtility.h" |
| #import "WXComponent+Layout.h" |
| #import "WXComponent+Events.h" |
| |
| typedef NS_ENUM(NSInteger, Direction) { |
| DirectionNone = 1 << 0, |
| DirectionLeft = 1 << 1, |
| DirectionRight = 1 << 2 |
| }; |
| |
| @class WXRecycleSliderView; |
| @class WXIndicatorView; |
| |
| @protocol WXRecycleSliderViewDelegate <UIScrollViewDelegate> |
| |
| - (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScroll:(UIScrollView *)scrollView; |
| - (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScrollToItemAtIndex:(NSInteger)index; |
| - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView; |
| - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; |
| - (BOOL)requestGestureShouldStopPropagation:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch; |
| |
| @end |
| |
| |
| @interface WXRecycleSliderView : UIView <UIScrollViewDelegate> |
| |
| @property (nonatomic, strong) WXIndicatorView *indicator; |
| @property (nonatomic, weak) id<WXRecycleSliderViewDelegate> delegate; |
| @property (nonatomic, strong) WXRecycleSliderScrollView *scrollView; |
| @property (nonatomic, strong) NSMutableArray *itemViews; |
| @property (nonatomic, assign) Direction direction; |
| @property (nonatomic, assign) NSInteger currentIndex; |
| @property (nonatomic, assign) NSInteger nextIndex; |
| @property (nonatomic, assign) CGRect currentItemFrame; |
| @property (nonatomic, assign) CGRect nextItemFrame; |
| @property (nonatomic, assign) BOOL infinite; |
| @property (nonatomic, assign) BOOL forbidSlideAnimation; |
| |
| - (void)insertItemView:(UIView *)view atIndex:(NSInteger)index; |
| - (void)removeItemView:(UIView *)view; |
| |
| @end |
| |
| @implementation WXRecycleSliderView |
| |
| - (id)initWithFrame:(CGRect)frame |
| { |
| self = [super initWithFrame:frame]; |
| if (self) { |
| _currentIndex = 0; |
| _itemViews = [[NSMutableArray alloc] init]; |
| _scrollView = [[WXRecycleSliderScrollView alloc] init]; |
| if (@available(iOS 11.0, *)) { |
| _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; |
| } |
| _scrollView.backgroundColor = [UIColor clearColor]; |
| _scrollView.delegate = self; |
| _scrollView.showsHorizontalScrollIndicator = NO; |
| _scrollView.showsVerticalScrollIndicator = NO; |
| _scrollView.scrollsToTop = NO; |
| [self addSubview:_scrollView]; |
| } |
| return self; |
| } |
| |
| - (void)dealloc |
| { |
| if (_scrollView) { |
| _scrollView.delegate = nil; |
| } |
| } |
| |
| - (void)layoutSubviews |
| { |
| [super layoutSubviews]; |
| [self resetAllViewsFrame]; |
| } |
| |
| - (void)accessibilityDecrement |
| { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Warc-performSelector-leaks" |
| [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(false)]; |
| #pragma clang diagnostic pop |
| |
| [self nextPage]; |
| } |
| |
| - (void)accessibilityIncrement |
| { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Warc-performSelector-leaks" |
| [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(false)]; |
| #pragma clang diagnostic pop |
| |
| [self lastPage]; |
| } |
| |
| - (void)accessibilityElementDidLoseFocus |
| { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Warc-performSelector-leaks" |
| [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(true)]; |
| #pragma clang diagnostic pop |
| } |
| |
| #pragma mark Private Methods |
| - (CGFloat)height { |
| return self.frame.size.height; |
| } |
| |
| - (CGFloat)width { |
| return self.frame.size.width; |
| } |
| |
| - (UIView *)getItemAtIndex:(NSInteger)index |
| { |
| if (self.itemViews.count > index) { |
| return [self.itemViews objectAtIndex:index]; |
| }else{ |
| return nil; |
| } |
| } |
| |
| - (void)setCurrentIndex:(NSInteger)currentIndex |
| { |
| if (currentIndex >= _itemViews.count || currentIndex < 0) { |
| currentIndex = 0; |
| } |
| NSInteger oldIndex = _currentIndex; |
| _currentIndex = currentIndex; |
| if (_infinite) { |
| if (_direction == DirectionRight) { |
| self.nextItemFrame = CGRectMake(0, 0, self.width, self.height); |
| self.nextIndex = self.currentIndex - 1; |
| if (self.nextIndex < 0) |
| { |
| self.nextIndex = _itemViews.count - 1; |
| } |
| }else if (_direction == DirectionLeft) { |
| self.nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height); |
| self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0; |
| }else { |
| self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0; |
| } |
| [self resetAllViewsFrame]; |
| } else { |
| [_scrollView setContentOffset:CGPointMake(_currentIndex * self.width, 0) animated:!_forbidSlideAnimation]; |
| } |
| [self resetIndicatorPoint]; |
| if (self.delegate && [self.delegate respondsToSelector:@selector(recycleSliderView:didScrollToItemAtIndex:)]) { |
| if (oldIndex != _currentIndex) { |
| [self.delegate recycleSliderView:self didScrollToItemAtIndex:_currentIndex]; |
| } |
| } |
| } |
| |
| - (void)resetIndicatorPoint |
| { |
| [self.indicator setPointCount:self.itemViews.count]; |
| [self.indicator setCurrentPoint:_currentIndex]; |
| } |
| |
| #pragma mark Scroll & Frames |
| - (void)setDirection:(Direction)direction { |
| if (_direction == direction) return; |
| _direction = direction; |
| if (_direction == DirectionNone) return; |
| if (_direction == DirectionRight) { |
| self.nextItemFrame = CGRectMake(0, 0, self.width, self.height); |
| self.nextIndex = self.currentIndex - 1; |
| if (self.nextIndex < 0) |
| { |
| self.nextIndex = _itemViews.count - 1; |
| } |
| UIView *view = [self getItemAtIndex:_nextIndex]; |
| if (view) { |
| view.frame = _nextItemFrame; |
| } |
| }else if (_direction == DirectionLeft){ |
| self.nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height); |
| self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0; |
| UIView *view = [self getItemAtIndex:_nextIndex]; |
| if (view) { |
| view.frame = _nextItemFrame; |
| } |
| } |
| } |
| |
| - (void)resetAllViewsFrame |
| { |
| if (_infinite && _itemViews.count > 1) { |
| self.scrollView.frame = CGRectMake(0, 0, self.width, self.height); |
| self.scrollView.contentOffset = CGPointMake(self.width, 0); |
| if (self.itemViews.count > 1) { |
| self.scrollView.contentSize = CGSizeMake(self.width * 3, 0); |
| } else { |
| self.scrollView.contentSize = CGSizeZero; |
| } |
| _currentItemFrame = CGRectMake(self.width, 0, self.width, self.height); |
| for (int i = 0; i < self.itemViews.count; i++) { |
| UIView *view = [self.itemViews objectAtIndex:i]; |
| if (i != self.currentIndex) { |
| view.frame = CGRectMake(self.frame.size.width * 3, 0, self.width, self.height);; |
| } |
| } |
| [self getItemAtIndex:_currentIndex].frame = _currentItemFrame; |
| if (_itemViews.count == 2) { |
| _nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height); |
| [self getItemAtIndex:_nextIndex].frame = _nextItemFrame; |
| } |
| } else { |
| self.scrollView.frame = self.bounds; |
| self.scrollView.contentSize = CGSizeMake(self.width * _itemViews.count, self.height); |
| self.scrollView.contentOffset = CGPointMake(_currentIndex * self.width, 0); |
| for (int i = 0; i < _itemViews.count; i ++) { |
| UIView *view = [_itemViews objectAtIndex:i]; |
| view.frame = CGRectMake(i * self.width, 0, self.width, self.height); |
| } |
| [self.scrollView setContentOffset:CGPointMake(_currentIndex * self.width, 0) animated:NO]; |
| } |
| [self resetIndicatorPoint]; |
| } |
| |
| - (void)nextPage { |
| if (_itemViews.count > 1) { |
| if (_infinite) { |
| [self.scrollView setContentOffset:CGPointMake(self.width * 2, 0) animated:!_forbidSlideAnimation]; |
| } else { |
| // the currentindex will be set at the end of animation |
| NSInteger nextIndex = self.currentIndex + 1; |
| if(nextIndex < _itemViews.count) { |
| [self.scrollView setContentOffset:CGPointMake(nextIndex * self.width, 0) animated:!_forbidSlideAnimation]; |
| } |
| } |
| } |
| } |
| |
| - (void)lastPage |
| { |
| NSInteger lastIndex = [self currentIndex]-1; |
| if (_itemViews.count > 1) { |
| if (_infinite) { |
| if (lastIndex < 0) { |
| lastIndex = [_itemViews count]-1; |
| } |
| } |
| [self setCurrentIndex:lastIndex]; |
| } |
| } |
| |
| - (void)resetScrollView |
| { |
| if (WXFloatEqual(self.scrollView.contentOffset.x / self.width , 1.0)) |
| { |
| return; |
| } |
| [self setCurrentIndex:self.nextIndex]; |
| self.scrollView.contentOffset = CGPointMake(self.width, 0); |
| } |
| |
| #pragma mark Public Methods |
| |
| - (void)setIndicator:(WXIndicatorView *)indicator |
| { |
| _indicator = indicator; |
| [_indicator setPointCount:self.itemViews.count]; |
| [_indicator setCurrentPoint:_currentIndex]; |
| } |
| |
| - (void)insertItemView:(UIView *)view atIndex:(NSInteger)index |
| { |
| if (![self.itemViews containsObject:view]) { |
| view.tag = self.itemViews.count; |
| if (index < 0) { |
| [self.itemViews addObject:view]; |
| } else { |
| [self.itemViews insertObject:view atIndex:index]; |
| } |
| } |
| |
| if (![self.scrollView.subviews containsObject:view]) { |
| if (index < 0) { |
| [self.scrollView addSubview:view]; |
| } else { |
| [self.scrollView insertSubview:view atIndex:index]; |
| } |
| } |
| [self layoutSubviews]; |
| [self setCurrentIndex:_currentIndex]; |
| } |
| |
| - (void)removeItemView:(UIView *)view |
| { |
| if ([self.itemViews containsObject:view]) { |
| [self.itemViews removeObject:view]; |
| } |
| |
| if ([self.scrollView.subviews containsObject:view]) { |
| [view removeFromSuperview]; |
| } |
| [self layoutSubviews]; |
| [self setCurrentIndex:_currentIndex]; |
| } |
| |
| #pragma mark ScrollView Delegate |
| |
| - (void)scrollViewDidScroll:(UIScrollView *)scrollView |
| { |
| if (_infinite) { |
| CGFloat offX = scrollView.contentOffset.x; |
| self.direction = offX > self.width ? DirectionLeft : offX < self.width ? DirectionRight : DirectionNone; |
| } |
| if (self.delegate && [self.delegate respondsToSelector:@selector(recycleSliderView:didScroll:)]) { |
| [self.delegate recycleSliderView:self didScroll:self.scrollView]; |
| } |
| } |
| |
| - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView |
| { |
| if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { |
| [self.delegate scrollViewWillBeginDragging:self.scrollView]; |
| } |
| } |
| |
| - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate |
| { |
| if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewDidEndDragging: willDecelerate:)]) { |
| [self.delegate scrollViewDidEndDragging:self.scrollView willDecelerate:decelerate]; |
| } |
| } |
| |
| - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { |
| if (_infinite) { |
| [self resetScrollView]; |
| } else { |
| NSInteger index = _scrollView.contentOffset.x / self.width; |
| [self setCurrentIndex:index]; |
| } |
| } |
| |
| - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { |
| /* In this case, we forbid animation temporarily so that |
| setContentOffset in setCurrentIndex won't cause endless loop |
| on some devices. |
| We have to use _forbidSlideAnimation in setCurrentIndex because |
| sometimes JS will trigger the slider to slide to some posistion |
| with animation. |
| */ |
| BOOL oldValue = _forbidSlideAnimation; |
| _forbidSlideAnimation = YES; |
| |
| if (_infinite) { |
| [self resetScrollView]; |
| } else { |
| NSInteger index = _scrollView.contentOffset.x / self.width; |
| [self setCurrentIndex:index]; |
| } |
| |
| _forbidSlideAnimation = oldValue; |
| } |
| |
| @end |
| |
| @interface WXCycleSliderComponent () <WXRecycleSliderViewDelegate,WXIndicatorComponentDelegate> |
| |
| @property (nonatomic, weak) WXRecycleSliderView *recycleSliderView; |
| @property (nonatomic, strong) NSTimer *autoTimer; |
| @property (nonatomic, assign) NSInteger currentIndex; |
| @property (nonatomic, assign) BOOL autoPlay; |
| @property (nonatomic, assign) NSUInteger interval; |
| @property (nonatomic, assign) NSInteger index; |
| @property (nonatomic, assign) CGFloat lastOffsetXRatio; |
| @property (nonatomic, assign) CGFloat offsetXAccuracy; |
| @property (nonatomic, assign) BOOL sliderChangeEvent; |
| @property (nonatomic, assign) BOOL sliderScrollEvent; |
| @property (nonatomic, assign) BOOL sliderScrollStartEvent; |
| @property (nonatomic, assign) BOOL sliderScrollEndEvent; |
| @property (nonatomic, assign) BOOL sliderStartEventFired; |
| @property (nonatomic, strong) NSMutableArray *childrenView; |
| @property (nonatomic, assign) BOOL scrollable; |
| @property (nonatomic, assign) BOOL infinite; |
| @property (nonatomic, assign) BOOL forbidSlideAnimation; |
| |
| @end |
| |
| @implementation WXCycleSliderComponent |
| |
| - (void) dealloc |
| { |
| [self _stopAutoPlayTimer]; |
| } |
| |
| - (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; |
| _sliderScrollEvent = NO; |
| _interval = 3000; |
| _childrenView = [NSMutableArray new]; |
| _lastOffsetXRatio = 0; |
| |
| if (attributes[@"autoPlay"]) { |
| _autoPlay = [WXConvert BOOL:attributes[@"autoPlay"]]; |
| } |
| |
| if (attributes[@"interval"]) { |
| _interval = [WXConvert NSInteger:attributes[@"interval"]]; |
| } |
| |
| if (attributes[@"index"]) { |
| _index = [WXConvert NSInteger:attributes[@"index"]]; |
| } |
| _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES; |
| if (attributes[@"offsetXAccuracy"]) { |
| _offsetXAccuracy = [WXConvert CGFloat:attributes[@"offsetXAccuracy"]]; |
| } |
| _infinite = attributes[@"infinite"] ? [WXConvert BOOL:attributes[@"infinite"]] : YES; |
| |
| _forbidSlideAnimation = attributes[@"forbidSlideAnimation"] ? [WXConvert BOOL:attributes[@"forbidSlideAnimation"]] : NO; |
| |
| self.flexCssNode->setFlexDirection(WeexCore::kFlexDirectionRow,NO); |
| } |
| return self; |
| } |
| |
| - (UIView *)loadView |
| { |
| return [[WXRecycleSliderView alloc] init]; |
| } |
| |
| - (void)viewDidLoad |
| { |
| [super viewDidLoad]; |
| |
| _recycleSliderView = (WXRecycleSliderView *)self.view; |
| _recycleSliderView.delegate = self; |
| _recycleSliderView.scrollView.pagingEnabled = YES; |
| _recycleSliderView.exclusiveTouch = YES; |
| _recycleSliderView.scrollView.scrollEnabled = _scrollable; |
| _recycleSliderView.infinite = _infinite; |
| _recycleSliderView.forbidSlideAnimation = _forbidSlideAnimation; |
| UIAccessibilityTraits traits = UIAccessibilityTraitAdjustable; |
| if (_autoPlay) { |
| traits |= UIAccessibilityTraitUpdatesFrequently; |
| [self _startAutoPlayTimer]; |
| } else { |
| [self _stopAutoPlayTimer]; |
| } |
| _recycleSliderView.accessibilityTraits = traits; |
| } |
| |
| - (void)layoutDidFinish |
| { |
| _recycleSliderView.currentIndex = self.currentIndex; |
| } |
| |
| - (void)_buildViewHierarchyLazily { |
| [super _buildViewHierarchyLazily]; |
| _recycleSliderView.currentIndex = self.currentIndex; |
| } |
| |
| - (void)adjustForRTL |
| { |
| if (![WXUtility enableRTLLayoutDirection]) return; |
| |
| // this is scroll rtl solution. |
| // scroll layout not use direction, use self tranform |
| if (self.view && self.isDirectionRTL) { |
| WXRecycleSliderView *slider = (WXRecycleSliderView *)self.view; |
| CATransform3D transform = CATransform3DScale(CATransform3DIdentity, -1, 1, 1); |
| slider.scrollView.layer.transform = transform ; |
| } else { |
| WXRecycleSliderView *slider = (WXRecycleSliderView *)self.view; |
| slider.scrollView.layer.transform = CATransform3DIdentity ; |
| } |
| |
| } |
| |
| - (void)_adjustForRTL { |
| if (![WXUtility enableRTLLayoutDirection]) return; |
| |
| [super _adjustForRTL]; |
| [self adjustForRTL]; |
| } |
| |
| - (BOOL)shouldTransformSubviewsWhenRTL { |
| return YES; |
| } |
| |
| - (void)viewDidUnload |
| { |
| [_childrenView removeAllObjects]; |
| } |
| |
| - (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; |
| |
| if(index < 0) { |
| [self.childrenView addObject:view]; |
| } |
| else { |
| [self.childrenView insertObject:view atIndex:index]; |
| } |
| |
| WXRecycleSliderView *recycleSliderView = (WXRecycleSliderView *)self.view; |
| if ([view isKindOfClass:[WXIndicatorView class]]) { |
| ((WXIndicatorComponent *)subcomponent).delegate = self; |
| [recycleSliderView addSubview:view]; |
| [self setIndicatorView:(WXIndicatorView *)view]; |
| return; |
| } |
| |
| subcomponent.isViewFrameSyncWithCalculated = NO; |
| |
| if (index == -1) { |
| [recycleSliderView insertItemView:view atIndex:index]; |
| } else { |
| NSInteger offset = 0; |
| for (int i = 0; i < [self.childrenView count]; ++i) { |
| if (index == i) break; |
| |
| if ([self.childrenView[i] isKindOfClass:[WXIndicatorView class]]) { |
| offset++; |
| } |
| } |
| [recycleSliderView insertItemView:view atIndex:index - offset]; |
| |
| // check if should apply current contentOffset |
| // in case inserting subviews after layoutDidFinish |
| if (index-offset == _index && _index>0) { |
| recycleSliderView.currentIndex = _index; |
| } |
| } |
| [recycleSliderView layoutSubviews]; |
| } |
| } |
| |
| - (void)willRemoveSubview:(WXComponent *)component |
| { |
| UIView *view = component.view; |
| |
| if(self.childrenView && [self.childrenView containsObject:view]) { |
| [self.childrenView removeObject:view]; |
| } |
| |
| WXRecycleSliderView *recycleSliderView = (WXRecycleSliderView *)_view; |
| [recycleSliderView removeItemView:view]; |
| if (self.childrenView.count <= _index) { |
| [recycleSliderView setCurrentIndex:0]; |
| } |
| } |
| |
| - (void)updateAttributes:(NSDictionary *)attributes |
| { |
| if (attributes[@"forbidSlideAnimation"]) { |
| _forbidSlideAnimation = [WXConvert BOOL:attributes[@"forbidSlideAnimation"]]; |
| _recycleSliderView.forbidSlideAnimation = _forbidSlideAnimation; |
| } |
| |
| if (attributes[@"autoPlay"]) { |
| _autoPlay = [WXConvert BOOL:attributes[@"autoPlay"]]; |
| if (_autoPlay) { |
| [self _startAutoPlayTimer]; |
| } else { |
| [self _stopAutoPlayTimer]; |
| } |
| } |
| |
| if (attributes[@"interval"]) { |
| _interval = [WXConvert NSInteger:attributes[@"interval"]]; |
| [self _stopAutoPlayTimer]; |
| |
| if (_autoPlay) { |
| [self _startAutoPlayTimer]; |
| } |
| } |
| |
| if (attributes[@"index"]) { |
| _index = [WXConvert NSInteger:attributes[@"index"]]; |
| self.currentIndex = _index; |
| self.recycleSliderView.currentIndex = _index; |
| } |
| |
| if (attributes[@"scrollable"]) { |
| _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES; |
| ((WXRecycleSliderView *)self.view).scrollView.scrollEnabled = _scrollable; |
| } |
| |
| if (attributes[@"offsetXAccuracy"]) { |
| _offsetXAccuracy = [WXConvert CGFloat:attributes[@"offsetXAccuracy"]]; |
| } |
| if (attributes[@"infinite"]) { |
| _infinite = [WXConvert BOOL:attributes[@"infinite"]]; |
| } |
| } |
| |
| - (void)addEvent:(NSString *)eventName |
| { |
| if ([eventName isEqualToString:@"change"]) { |
| _sliderChangeEvent = YES; |
| } |
| if ([eventName isEqualToString:@"scroll"]) { |
| _sliderScrollEvent = YES; |
| } |
| } |
| |
| - (void)removeEvent:(NSString *)eventName |
| { |
| if ([eventName isEqualToString:@"change"]) { |
| _sliderChangeEvent = NO; |
| } |
| if ([eventName isEqualToString:@"scroll"]) { |
| _sliderScrollEvent = NO; |
| } |
| } |
| |
| - (BOOL)requestGestureShouldStopPropagation:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch |
| { |
| return [self gestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch]; |
| } |
| |
| #pragma mark WXIndicatorComponentDelegate Methods |
| |
| -(void)setIndicatorView:(WXIndicatorView *)indicatorView |
| { |
| NSAssert(_recycleSliderView, @"");//!OCLint |
| [_recycleSliderView setIndicator:indicatorView]; |
| } |
| |
| - (void)resumeAutoPlay:(id)resume |
| { |
| if (_autoPlay) { |
| if ([resume boolValue]) { |
| [self _startAutoPlayTimer]; |
| } else { |
| [self _stopAutoPlayTimer]; |
| } |
| } |
| } |
| |
| #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 |
| { |
| if (!_infinite && (_currentIndex == _recycleSliderView.itemViews.count - 1)) { |
| [self _stopAutoPlayTimer]; |
| }else { |
| [self.recycleSliderView nextPage]; |
| } |
| } |
| |
| #pragma mark ScrollView Delegate |
| |
| - (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScroll:(UIScrollView *)scrollView |
| { |
| if (_sliderScrollEvent) { |
| CGFloat width = scrollView.frame.size.width; |
| CGFloat XDeviation = 0; |
| if (_infinite) { |
| XDeviation = - (scrollView.contentOffset.x - width); |
| } else { |
| XDeviation = - (scrollView.contentOffset.x - width * _currentIndex); |
| } |
| CGFloat offsetXRatio = (XDeviation / width); |
| if (fabs(offsetXRatio - _lastOffsetXRatio) >= _offsetXAccuracy) { |
| _lastOffsetXRatio = offsetXRatio; |
| [self fireEvent:@"scroll" params:@{@"offsetXRatio":[NSNumber numberWithFloat:offsetXRatio]} domChanges:nil]; |
| } |
| } |
| } |
| |
| - (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScrollToItemAtIndex:(NSInteger)index |
| { |
| if (_sliderChangeEvent) { |
| [self fireEvent:@"change" params:@{@"index":@(index)} domChanges:@{@"attrs": @{@"index": @(index)}}]; |
| } |
| self.currentIndex = index; |
| } |
| |
| - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView |
| { |
| [self _stopAutoPlayTimer]; |
| } |
| |
| - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate |
| { |
| if (_autoPlay) { |
| [self _startAutoPlayTimer]; |
| } |
| } |
| |
| @end |
| |
| @implementation WXRecycleSliderScrollView |
| - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch |
| { |
| WXRecycleSliderView *view = (WXRecycleSliderView *)self.delegate; |
| if (![view isKindOfClass:[UIView class]]) { |
| return YES; |
| } |
| |
| if ([(id <WXRecycleSliderViewDelegate>) view.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) { |
| return [(id <WXRecycleSliderViewDelegate>) view.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch]; |
| } |
| else{ |
| return YES; |
| } |
| } |
| @end |
| |