123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741 |
- //
- // UUMarqueeView.m
- // UUMarqueeView
- //
- // Created by youyou on 16/12/5.
- // Copyright © 2016年 iceyouyou. All rights reserved.
- //
- #import "UUMarqueeView.h"
- @interface UUMarqueeView () <UUMarqueeViewTouchResponder>
- @property (nonatomic, strong) UIView *contentView;
- @property (nonatomic, assign) NSInteger visibleItemCount;
- @property (nonatomic, strong) NSMutableArray<UUMarqueeItemView*> *items;
- @property (nonatomic, assign) int firstItemIndex;
- @property (nonatomic, assign) int dataIndex;
- @property (nonatomic, strong) NSTimer *scrollTimer;
- @property (nonatomic, strong) UUMarqueeViewTouchReceiver *touchReceiver;
- @property (nonatomic, assign) BOOL isWaiting;
- @property (nonatomic, assign) BOOL isScrolling;
- @property (nonatomic, assign) BOOL isScrollNeedsToStop;
- @property (nonatomic, assign) BOOL isPausingBeforeTouchesBegan;
- @property (nonatomic, assign) BOOL isPausingBeforeResignActive;
- @end
- @implementation UUMarqueeView
- static NSInteger const DEFAULT_VISIBLE_ITEM_COUNT = 2;
- static NSTimeInterval const DEFAULT_TIME_INTERVAL = 4.0;
- static NSTimeInterval const DEFAULT_TIME_DURATION = 1.0;
- static float const DEFAULT_SCROLL_SPEED = 40.0f;
- static float const DEFAULT_ITEM_SPACING = 20.0f;
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- if (self = [super initWithCoder:aDecoder]) {
- _timeIntervalPerScroll = DEFAULT_TIME_INTERVAL;
- _timeDurationPerScroll = DEFAULT_TIME_DURATION;
- _scrollSpeed = DEFAULT_SCROLL_SPEED;
- _itemSpacing = DEFAULT_ITEM_SPACING;
- _touchEnabled = NO;
- _stopWhenLessData = NO;
- _contentView = [[UIView alloc] initWithFrame:self.bounds];
- _contentView.clipsToBounds = YES;
- [self addSubview:_contentView];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(handleResignActiveNotification:)
- name:UIApplicationWillResignActiveNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(handleBecomeActiveNotification:)
- name:UIApplicationDidBecomeActiveNotification
- object:nil];
- }
- return self;
- }
- - (instancetype)initWithDirection:(UUMarqueeViewDirection)direction {
- if (self = [super init]) {
- _direction = direction;
- }
- return self;
- }
- - (instancetype)initWithFrame:(CGRect)frame direction:(UUMarqueeViewDirection)direction {
- if (self = [super initWithFrame:frame]) {
- _direction = direction;
- _timeIntervalPerScroll = DEFAULT_TIME_INTERVAL;
- _timeDurationPerScroll = DEFAULT_TIME_DURATION;
- _scrollSpeed = DEFAULT_SCROLL_SPEED;
- _itemSpacing = DEFAULT_ITEM_SPACING;
- _touchEnabled = NO;
- _stopWhenLessData = NO;
- _contentView = [[UIView alloc] initWithFrame:self.bounds];
- _contentView.clipsToBounds = YES;
- [self addSubview:_contentView];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(handleResignActiveNotification:)
- name:UIApplicationWillResignActiveNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(handleBecomeActiveNotification:)
- name:UIApplicationDidBecomeActiveNotification
- object:nil];
- }
- return self;
- }
- - (instancetype)initWithFrame:(CGRect)frame {
- if (self = [super initWithFrame:frame]) {
- _timeIntervalPerScroll = DEFAULT_TIME_INTERVAL;
- _timeDurationPerScroll = DEFAULT_TIME_DURATION;
- _scrollSpeed = DEFAULT_SCROLL_SPEED;
- _itemSpacing = DEFAULT_ITEM_SPACING;
- _touchEnabled = NO;
- _stopWhenLessData = NO;
- _contentView = [[UIView alloc] initWithFrame:self.bounds];
- _contentView.clipsToBounds = YES;
- [self addSubview:_contentView];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(handleResignActiveNotification:)
- name:UIApplicationWillResignActiveNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(handleBecomeActiveNotification:)
- name:UIApplicationDidBecomeActiveNotification
- object:nil];
- }
- return self;
- }
- - (void)setClipsToBounds:(BOOL)clipsToBounds {
- _contentView.clipsToBounds = clipsToBounds;
- }
- - (void)setTouchEnabled:(BOOL)touchEnabled {
- _touchEnabled = touchEnabled;
- [self resetTouchReceiver];
- }
- - (void)reloadData {
- if (_isWaiting) {
- if (_scrollTimer) {
- [_scrollTimer invalidate];
- self.scrollTimer = nil;
- }
- [self resetAll];
- [self startAfterTimeInterval:YES];
- } else if (_isScrolling) {
- [self resetAll];
- } else {
- // stopped
- [self resetAll];
- [self startAfterTimeInterval:YES];
- }
- }
- - (void)start {
- self.isScrollNeedsToStop = NO;
- if (!_isScrolling && !_isWaiting) {
- [self startAfterTimeInterval:NO];
- }
- }
- - (void)pause {
- self.isScrollNeedsToStop = YES;
- }
- - (void)repeat {
- if (!_isScrollNeedsToStop) {
- [self startAfterTimeInterval:YES];
- }
- }
- - (void)repeatWithAnimationFinished:(BOOL)finished {
- if (!_isScrollNeedsToStop) {
- [self startAfterTimeInterval:YES animationFinished:finished];
- }
- }
- - (void)startAfterTimeInterval:(BOOL)afterTimeInterval {
- [self startAfterTimeInterval:afterTimeInterval animationFinished:YES];
- }
- - (void)startAfterTimeInterval:(BOOL)afterTimeInterval animationFinished:(BOOL)finished {
- if (_isScrolling || _items.count <= 0) {
- return;
- }
- self.isWaiting = YES;
- NSTimeInterval timeInterval = 1.0;
- if (finished) {
- timeInterval = afterTimeInterval ? _timeIntervalPerScroll : 0.0;
- }
- self.scrollTimer = [NSTimer scheduledTimerWithTimeInterval:timeInterval
- target:self
- selector:@selector(scrollTimerDidFire:)
- userInfo:nil
- repeats:NO];
- }
- #pragma mark - Override(private)
- - (void)layoutSubviews {
- [super layoutSubviews];
- [_contentView setFrame:self.bounds];
- [self repositionItemViews];
- if (_touchEnabled && _touchReceiver) {
- [_touchReceiver setFrame:self.bounds];
- }
- }
- - (void)dealloc {
- if (_scrollTimer) {
- [_scrollTimer invalidate];
- self.scrollTimer = nil;
- }
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- #pragma mark - Notification
- - (void)handleResignActiveNotification:(NSNotification*)notification {
- self.isPausingBeforeResignActive = _isScrollNeedsToStop;
- [self pause];
- }
- - (void)handleBecomeActiveNotification:(NSNotification*)notification {
- if (!_isPausingBeforeResignActive) {
- [self start];
- }
- }
- #pragma mark - ItemView(private)
- - (void)resetAll {
- self.dataIndex = -1;
- self.firstItemIndex = 0;
- if (_items) {
- [_items makeObjectsPerformSelector:@selector(removeFromSuperview)];
- [_items removeAllObjects];
- } else {
- self.items = [NSMutableArray array];
- }
- if (_direction == UUMarqueeViewDirectionLeftward) {
- self.visibleItemCount = 1;
- } else {
- if ([_delegate respondsToSelector:@selector(numberOfVisibleItemsForMarqueeView:)]) {
- self.visibleItemCount = [_delegate numberOfVisibleItemsForMarqueeView:self];
- if (_visibleItemCount <= 0) {
- return;
- }
- } else {
- self.visibleItemCount = DEFAULT_VISIBLE_ITEM_COUNT;
- }
- }
- for (int i = 0; i < _visibleItemCount + 2; i++) {
- UUMarqueeItemView *itemView = [[UUMarqueeItemView alloc] init];
- [_contentView addSubview:itemView];
- [_items addObject:itemView];
- }
- if (_direction == UUMarqueeViewDirectionLeftward) {
- CGFloat itemHeight = CGRectGetHeight(self.frame) / _visibleItemCount;
- CGFloat lastMaxX = 0.0f;
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- CGFloat itemWidth = CGRectGetWidth(self.frame);
- if (i == 0) {
- [_items[index] setFrame:CGRectMake(-itemWidth, 0.0f, itemWidth, itemHeight)];
- lastMaxX = 0.0f;
- [self createItemView:_items[index]];
- } else {
- [self moveToNextDataIndex];
- _items[index].tag = _dataIndex;
- _items[index].width = [self itemWidthAtIndex:_items[index].tag];
- itemWidth = MAX(_items[index].width + _itemSpacing, itemWidth);
- [_items[index] setFrame:CGRectMake(lastMaxX, 0.0f, itemWidth, itemHeight)];
- lastMaxX = lastMaxX + itemWidth;
- [self updateItemView:_items[index] atIndex:_items[index].tag];
- }
- }
- } else {
- if (_useDynamicHeight) {
- CGFloat itemWidth = CGRectGetWidth(self.frame);
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- if (i == _items.count - 1) {
- [self moveToNextDataIndex];
- _items[index].tag = _dataIndex;
- _items[index].height = [self itemHeightAtIndex:_items[index].tag];
- _items[index].alpha = 0.0f;
- [_items[index] setFrame:CGRectMake(0.0f, CGRectGetMaxY(self.bounds), itemWidth, _items[index].height)];
- [self updateItemView:_items[index] atIndex:_items[index].tag];
- } else {
- _items[index].tag = _dataIndex;
- _items[index].alpha = 0.0f;
- [_items[index] setFrame:CGRectMake(0.0f, 0.0f, itemWidth, 0.0f)];
- }
- }
- } else {
- NSUInteger dataCount = 0;
- if ([_delegate respondsToSelector:@selector(numberOfDataForMarqueeView:)]) {
- dataCount = [_delegate numberOfDataForMarqueeView:self];
- }
- CGFloat itemWidth = CGRectGetWidth(self.frame);
- CGFloat itemHeight = CGRectGetHeight(self.frame) / _visibleItemCount;
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- if (i == 0) {
- _items[index].tag = _dataIndex;
- [_items[index] setFrame:CGRectMake(0.0f, -itemHeight, itemWidth, itemHeight)];
- [self createItemView:_items[index]];
- } else {
- [self moveToNextDataIndex];
- _items[index].tag = _dataIndex;
- [_items[index] setFrame:CGRectMake(0.0f, itemHeight * (i - 1), itemWidth, itemHeight)];
- if (_stopWhenLessData) {
- if (i <= dataCount) {
- [self updateItemView:_items[index] atIndex:_items[index].tag];
- } else {
- [self createItemView:_items[index]];
- }
- } else {
- [self updateItemView:_items[index] atIndex:_items[index].tag];
- }
- }
- }
- }
- }
- [self resetTouchReceiver];
- }
- - (void)repositionItemViews {
- if (_direction == UUMarqueeViewDirectionLeftward) {
- CGFloat itemHeight = CGRectGetHeight(self.frame) / _visibleItemCount;
- CGFloat lastMaxX = 0.0f;
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- CGFloat itemWidth = MAX(_items[index].width + _itemSpacing, CGRectGetWidth(self.frame));
- if (i == 0) {
- [_items[index] setFrame:CGRectMake(-itemWidth, 0.0f, itemWidth, itemHeight)];
- lastMaxX = 0.0f;
- } else {
- [_items[index] setFrame:CGRectMake(lastMaxX, 0.0f, itemWidth, itemHeight)];
- lastMaxX = lastMaxX + itemWidth;
- }
- }
- } else {
- if (_useDynamicHeight) {
- CGFloat itemWidth = CGRectGetWidth(self.frame);
- CGFloat lastMaxY = 0.0f;
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- if (i == 0) {
- [_items[index] setFrame:CGRectMake(0.0f, 0.0f, itemWidth, 0.0f)];
- lastMaxY = 0.0f;
- } else if (i == _items.count - 1) {
- [_items[index] setFrame:CGRectMake(0.0f, CGRectGetMaxY(self.bounds), itemWidth, _items[index].height)];
- } else {
- [_items[index] setFrame:CGRectMake(0.0f, lastMaxY, itemWidth, _items[index].height)];
- lastMaxY = lastMaxY + _items[index].height;
- }
- }
- CGFloat offsetY = CGRectGetHeight(self.frame) - lastMaxY;
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- if (i > 0 && i < _items.count - 1) {
- [_items[index] setFrame:CGRectMake(CGRectGetMinX(_items[index].frame),
- CGRectGetMinY(_items[index].frame) + offsetY,
- itemWidth,
- _items[index].height)];
- }
- }
- } else {
- CGFloat itemWidth = CGRectGetWidth(self.frame);
- CGFloat itemHeight = CGRectGetHeight(self.frame) / _visibleItemCount;
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- if (i == 0) {
- [_items[index] setFrame:CGRectMake(0.0f, -itemHeight, itemWidth, itemHeight)];
- } else {
- [_items[index] setFrame:CGRectMake(0.0f, itemHeight * (i - 1), itemWidth, itemHeight)];
- }
- }
- }
- }
- }
- - (int)itemIndexWithOffsetFromTop:(int)offsetFromTop {
- return (_firstItemIndex + offsetFromTop) % (_visibleItemCount + 2);
- }
- - (void)moveToNextItemIndex {
- if (_firstItemIndex >= _items.count - 1) {
- self.firstItemIndex = 0;
- } else {
- self.firstItemIndex++;
- }
- }
- - (CGFloat)itemWidthAtIndex:(NSInteger)index {
- CGFloat itemWidth = 0.0f;
- if (index >= 0) {
- if ([_delegate respondsToSelector:@selector(itemViewWidthAtIndex:forMarqueeView:)]) {
- itemWidth = [_delegate itemViewWidthAtIndex:index forMarqueeView:self];
- }
- }
- return itemWidth;
- }
- - (CGFloat)itemHeightAtIndex:(NSInteger)index {
- CGFloat itemHeight = 0.0f;
- if (index >= 0) {
- if ([_delegate respondsToSelector:@selector(itemViewHeightAtIndex:forMarqueeView:)]) {
- itemHeight = [_delegate itemViewHeightAtIndex:index forMarqueeView:self];
- }
- }
- return itemHeight;
- }
- - (void)createItemView:(UUMarqueeItemView*)itemView {
- if (!itemView.didFinishCreate) {
- if ([_delegate respondsToSelector:@selector(createItemView:forMarqueeView:)]) {
- [_delegate createItemView:itemView forMarqueeView:self];
- itemView.didFinishCreate = YES;
- }
- }
- }
- - (void)updateItemView:(UUMarqueeItemView*)itemView atIndex:(NSInteger)index {
- if (index < 0) {
- [itemView clear];
- }
- if (!itemView.didFinishCreate) {
- if ([_delegate respondsToSelector:@selector(createItemView:forMarqueeView:)]) {
- [_delegate createItemView:itemView forMarqueeView:self];
- itemView.didFinishCreate = YES;
- }
- }
- if (index >= 0) {
- if ([_delegate respondsToSelector:@selector(updateItemView:atIndex:forMarqueeView:)]) {
- [_delegate updateItemView:itemView atIndex:index forMarqueeView:self];
- }
- }
- }
- #pragma mark - Timer & Animation(private)
- - (void)scrollTimerDidFire:(NSTimer *)timer {
- self.isWaiting = NO;
- if (_isScrollNeedsToStop) {
- return;
- }
- self.isScrolling = YES;
- if (_stopWhenLessData) {
- NSUInteger dataCount = 0;
- if ([_delegate respondsToSelector:@selector(numberOfDataForMarqueeView:)]) {
- dataCount = [_delegate numberOfDataForMarqueeView:self];
- }
- if (_direction == UUMarqueeViewDirectionLeftward) {
- if (dataCount <= 1) {
- CGFloat itemWidth = MAX(_items[1].width + _itemSpacing, CGRectGetWidth(self.frame));
- if (itemWidth <= CGRectGetWidth(self.frame)) {
- __weak __typeof(self) weakSelf = self;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeDurationPerScroll * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- __strong __typeof(self) self = weakSelf;
- if (self) {
- self.isScrolling = NO;
- [self repeat];
- }
- });
- return;
- }
- }
- } else {
- if (dataCount <= _visibleItemCount) {
- __weak __typeof(self) weakSelf = self;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeDurationPerScroll * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- __strong __typeof(self) self = weakSelf;
- if (self) {
- self.isScrolling = NO;
- [self repeat];
- }
- });
- return;
- }
- }
- }
- dispatch_async(dispatch_get_main_queue(), ^() {
- if (_direction == UUMarqueeViewDirectionLeftward) {
- [self moveToNextDataIndex];
- CGFloat itemHeight = CGRectGetHeight(self.frame);
- CGFloat firstItemWidth = CGRectGetWidth(self.frame);
- CGFloat currentItemWidth = CGRectGetWidth(self.frame);
- CGFloat lastItemWidth = CGRectGetWidth(self.frame);
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- CGFloat itemWidth = MAX(_items[index].width + _itemSpacing, CGRectGetWidth(self.frame));
- if (i == 0) {
- firstItemWidth = itemWidth;
- } else if (i == 1) {
- currentItemWidth = itemWidth;
- } else {
- lastItemWidth = itemWidth;
- }
- }
- // move the left item to right without animation
- _items[_firstItemIndex].tag = _dataIndex;
- _items[_firstItemIndex].width = [self itemWidthAtIndex:_items[_firstItemIndex].tag];
- CGFloat nextItemWidth = MAX(_items[_firstItemIndex].width + _itemSpacing, CGRectGetWidth(self.frame));
- [_items[_firstItemIndex] setFrame:CGRectMake(lastItemWidth, 0.0f, nextItemWidth, itemHeight)];
- if (firstItemWidth != nextItemWidth) {
- // if the width of next item view changes, then recreate it by delegate
- [_items[_firstItemIndex] clear];
- }
- [self updateItemView:_items[_firstItemIndex] atIndex:_items[_firstItemIndex].tag];
- __weak __typeof(self) weakSelf = self;
- [UIView animateWithDuration:(currentItemWidth / _scrollSpeed) delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
- CGFloat lastMaxX = 0.0f;
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- CGFloat itemWidth = MAX(_items[index].width + _itemSpacing, CGRectGetWidth(self.frame));
- if (i == 0) {
- continue;
- } else if (i == 1) {
- [_items[index] setFrame:CGRectMake(-itemWidth, 0.0f, itemWidth, itemHeight)];
- lastMaxX = 0.0f;
- } else {
- [_items[index] setFrame:CGRectMake(lastMaxX, 0.0f, itemWidth, itemHeight)];
- lastMaxX = lastMaxX + itemWidth;
- }
- }
- } completion:^(BOOL finished) {
- __strong __typeof(self) self = weakSelf;
- if (self) {
- self.isScrolling = NO;
- [self repeatWithAnimationFinished:finished];
- }
- }];
- [self moveToNextItemIndex];
- } else {
- [self moveToNextDataIndex];
- CGFloat itemWidth = CGRectGetWidth(self.frame);
- CGFloat itemHeight = CGRectGetHeight(self.frame) / _visibleItemCount;
- // move the top item to bottom without animation
- _items[_firstItemIndex].tag = _dataIndex;
- if (_useDynamicHeight) {
- CGFloat firstItemWidth = _items[_firstItemIndex].height;
- _items[_firstItemIndex].height = [self itemHeightAtIndex:_items[_firstItemIndex].tag];
- [_items[_firstItemIndex] setFrame:CGRectMake(0.0f, CGRectGetMaxY(self.bounds), itemWidth, _items[_firstItemIndex].height)];
- if (firstItemWidth != _items[_firstItemIndex].height) {
- // if the height of next item view changes, then recreate it by delegate
- [_items[_firstItemIndex] clear];
- }
- } else {
- [_items[_firstItemIndex] setFrame:CGRectMake(0.0f, CGRectGetMaxY(self.bounds), itemWidth, itemHeight)];
- }
- [self updateItemView:_items[_firstItemIndex] atIndex:_items[_firstItemIndex].tag];
- if (_useDynamicHeight) {
- int lastItemIndex = (int)(_items.count - 1 + _firstItemIndex) % _items.count;
- CGFloat lastItemHeight = _items[lastItemIndex].height;
- __weak __typeof(self) weakSelf = self;
- [UIView animateWithDuration:(lastItemHeight / _scrollSpeed) delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- if (i == 0) {
- continue;
- } else if (i == 1) {
- [_items[index] setFrame:CGRectMake(CGRectGetMinX(_items[index].frame),
- CGRectGetMinY(_items[index].frame) - lastItemHeight,
- itemWidth,
- _items[index].height)];
- _items[index].alpha = 0.0f;
- } else {
- [_items[index] setFrame:CGRectMake(CGRectGetMinX(_items[index].frame),
- CGRectGetMinY(_items[index].frame) - lastItemHeight,
- itemWidth,
- _items[index].height)];
- _items[index].alpha = 1.0f;
- }
- }
- } completion:^(BOOL finished) {
- __strong __typeof(self) self = weakSelf;
- if (self) {
- self.isScrolling = NO;
- [self repeatWithAnimationFinished:finished];
- }
- }];
- } else {
- UIViewAnimationOptions animationOptions = UIViewAnimationOptionCurveEaseInOut;
- if (_timeIntervalPerScroll <= 0.0) {
- // smooth animation
- animationOptions = UIViewAnimationOptionCurveLinear;
- }
- __weak __typeof(self) weakSelf = self;
- [UIView animateWithDuration:_timeDurationPerScroll delay:0.0 options:animationOptions animations:^{
- for (int i = 0; i < _items.count; i++) {
- int index = (i + _firstItemIndex) % _items.count;
- if (i == 0) {
- continue;
- } else if (i == 1) {
- [_items[index] setFrame:CGRectMake(0.0f, -itemHeight, itemWidth, itemHeight)];
- } else {
- [_items[index] setFrame:CGRectMake(0.0f, itemHeight * (i - 2), itemWidth, itemHeight)];
- }
- }
- } completion:^(BOOL finished) {
- __strong __typeof(self) self = weakSelf;
- if (self) {
- self.isScrolling = NO;
- [self repeatWithAnimationFinished:finished];
- }
- }];
- }
- [self moveToNextItemIndex];
- }
- });
- }
- #pragma mark - Data source(private)
- - (void)moveToNextDataIndex {
- NSUInteger dataCount = 0;
- if ([_delegate respondsToSelector:@selector(numberOfDataForMarqueeView:)]) {
- dataCount = [_delegate numberOfDataForMarqueeView:self];
- }
- if (dataCount <= 0) {
- self.dataIndex = -1;
- } else {
- self.dataIndex = _dataIndex + 1;
- if (_dataIndex < 0 || _dataIndex > dataCount - 1) {
- self.dataIndex = 0;
- }
- }
- }
- #pragma mark - Touch handler(private)
- - (void)resetTouchReceiver {
- if (_touchEnabled) {
- if (!_touchReceiver) {
- self.touchReceiver = [[UUMarqueeViewTouchReceiver alloc] init];
- _touchReceiver.touchDelegate = self;
- [self addSubview:_touchReceiver];
- } else {
- [self bringSubviewToFront:_touchReceiver];
- }
- } else {
- if (_touchReceiver) {
- [_touchReceiver removeFromSuperview];
- self.touchReceiver = nil;
- }
- }
- }
- #pragma mark - UUMarqueeViewTouchResponder(private)
- - (void)touchesBegan {
- self.isPausingBeforeTouchesBegan = _isScrollNeedsToStop;
- [self pause];
- }
- - (void)touchesEndedAtPoint:(CGPoint)point {
- for (UUMarqueeItemView *itemView in _items) {
- if ([itemView.layer.presentationLayer hitTest:point]) {
- NSUInteger dataCount = 0;
- if ([_delegate respondsToSelector:@selector(numberOfDataForMarqueeView:)]) {
- dataCount = [_delegate numberOfDataForMarqueeView:self];
- }
- if (dataCount > 0 && itemView.tag >= 0 && itemView.tag < dataCount) {
- if ([self.delegate respondsToSelector:@selector(didTouchItemViewAtIndex:forMarqueeView:)]) {
- [self.delegate didTouchItemViewAtIndex:itemView.tag forMarqueeView:self];
- }
- }
- break;
- }
- }
- if (!_isPausingBeforeTouchesBegan) {
- [self start];
- }
- }
- - (void)touchesCancelled {
- if (!_isPausingBeforeTouchesBegan) {
- [self start];
- }
- }
- @end
- #pragma mark - UUMarqueeViewTouchReceiver(private)
- @implementation UUMarqueeViewTouchReceiver
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
- if (_touchDelegate) {
- [_touchDelegate touchesBegan];
- }
- }
- - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
- UITouch *touch = [touches anyObject];
- CGPoint touchLocation = [touch locationInView:self];
- if (_touchDelegate) {
- [_touchDelegate touchesEndedAtPoint:touchLocation];
- }
- }
- - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
- if (_touchDelegate) {
- [_touchDelegate touchesCancelled];
- }
- }
- @end
- #pragma mark - UUMarqueeItemView(Private)
- @implementation UUMarqueeItemView
- - (void)clear {
- [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
- _didFinishCreate = NO;
- }
- @end
|