Files

701 lines
24 KiB
Mathematica
Raw Permalink Normal View History

2025-08-11 10:43:19 +08:00
//
// SDCycleScrollView.m
// SDCycleScrollView
//
// Created by aier on 15-3-22.
// Copyright (c) 2015 GSD. All rights reserved.
//
/*
*********************************************************************************
*
* 🌟🌟🌟 SDCycleScrollViewQQ185534916 🌟🌟🌟
*
* 使bugbug
*
* :GSD_iOS
* Email : gsdios@126.com
* GitHub: https://github.com/gsdios
*
* SDAutoLayout
* CellTableviewLabelScrollView
* AutoLayout
* http://www.letv.com/ptv/vplay/24038772.html
* https://github.com/gsdios/SDAutoLayout/blob/master/README.md
* GitHubhttps://github.com/gsdios/SDAutoLayout
*********************************************************************************
*/
#import "SDCycleScrollView.h"
#import "SDCollectionViewCell.h"
#import "UIView+SDExtension.h"
#import "TAPageControl.h"
#import "SDWebImageManager.h"
#import "UIImageView+WebCache.h"
#define kCycleScrollViewInitialPageControlDotSize CGSizeMake(10, 10)
NSString * const ID = @"SDCycleScrollViewCell";
@interface SDCycleScrollView () <UICollectionViewDataSource, UICollectionViewDelegate>
@property (nonatomic, weak) UICollectionView *mainView; // collectionView
@property (nonatomic, weak) UICollectionViewFlowLayout *flowLayout;
@property (nonatomic, strong) NSArray *imagePathsGroup;
@property (nonatomic, weak) NSTimer *timer;
@property (nonatomic, assign) NSInteger totalItemsCount;
@property (nonatomic, weak) UIControl *pageControl;
@property (nonatomic, strong) UIImageView *backgroundImageView; // imageURLs
@end
@implementation SDCycleScrollView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self initialization];
[self setupMainView];
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self initialization];
[self setupMainView];
}
- (void)initialization
{
_pageControlAliment = SDCycleScrollViewPageContolAlimentCenter;
_autoScrollTimeInterval = 3.0;
_titleLabelTextColor = [UIColor whiteColor];
_titleLabelTextFont= [UIFont systemFontOfSize:14];
_titleLabelBackgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
_titleLabelHeight = 30;
_titleLabelTextAlignment = NSTextAlignmentLeft;
_autoScroll = YES;
_infiniteLoop = YES;
_showPageControl = YES;
_pageControlDotSize = kCycleScrollViewInitialPageControlDotSize;
_pageControlBottomOffset = 0;
_pageControlRightOffset = 0;
_pageControlStyle = SDCycleScrollViewPageContolStyleClassic;
_hidesForSinglePage = YES;
_currentPageDotColor = [UIColor whiteColor];
_pageDotColor = [UIColor lightGrayColor];
_bannerImageViewContentMode = UIViewContentModeScaleAspectFill;
self.backgroundColor = [UIColor clearColor];
}
+ (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageNamesGroup:(NSArray *)imageNamesGroup
{
SDCycleScrollView *cycleScrollView = [[self alloc] initWithFrame:frame];
cycleScrollView.localizationImageNamesGroup = [NSMutableArray arrayWithArray:imageNamesGroup];
return cycleScrollView;
}
+ (instancetype)cycleScrollViewWithFrame:(CGRect)frame shouldInfiniteLoop:(BOOL)infiniteLoop imageNamesGroup:(NSArray *)imageNamesGroup
{
SDCycleScrollView *cycleScrollView = [[self alloc] initWithFrame:frame];
cycleScrollView.infiniteLoop = infiniteLoop;
cycleScrollView.localizationImageNamesGroup = [NSMutableArray arrayWithArray:imageNamesGroup];
return cycleScrollView;
}
+ (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageURLStringsGroup:(NSArray *)imageURLsGroup
{
SDCycleScrollView *cycleScrollView = [[self alloc] initWithFrame:frame];
cycleScrollView.imageURLStringsGroup = [NSMutableArray arrayWithArray:imageURLsGroup];
return cycleScrollView;
}
+ (instancetype)cycleScrollViewWithFrame:(CGRect)frame delegate:(id<SDCycleScrollViewDelegate>)delegate placeholderImage:(UIImage *)placeholderImage
{
SDCycleScrollView *cycleScrollView = [[self alloc] initWithFrame:frame];
cycleScrollView.delegate = delegate;
cycleScrollView.placeholderImage = placeholderImage;
return cycleScrollView;
}
// collectionView
- (void)setupMainView
{
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.minimumLineSpacing = 0;
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_flowLayout = flowLayout;
UICollectionView *mainView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:flowLayout];
mainView.backgroundColor = [UIColor clearColor];
mainView.pagingEnabled = YES;
mainView.showsHorizontalScrollIndicator = NO;
mainView.showsVerticalScrollIndicator = NO;
[mainView registerClass:[SDCollectionViewCell class] forCellWithReuseIdentifier:ID];
mainView.dataSource = self;
mainView.delegate = self;
mainView.scrollsToTop = NO;
[self addSubview:mainView];
_mainView = mainView;
}
#pragma mark - properties
- (void)setDelegate:(id<SDCycleScrollViewDelegate>)delegate
{
_delegate = delegate;
if ([self.delegate respondsToSelector:@selector(customCollectionViewCellClassForCycleScrollView:)] && [self.delegate customCollectionViewCellClassForCycleScrollView:self]) {
[self.mainView registerClass:[self.delegate customCollectionViewCellClassForCycleScrollView:self] forCellWithReuseIdentifier:ID];
}else if ([self.delegate respondsToSelector:@selector(customCollectionViewCellNibForCycleScrollView:)] && [self.delegate customCollectionViewCellNibForCycleScrollView:self]) {
[self.mainView registerNib:[self.delegate customCollectionViewCellNibForCycleScrollView:self] forCellWithReuseIdentifier:ID];
}
}
- (void)setPlaceholderImage:(UIImage *)placeholderImage
{
_placeholderImage = placeholderImage;
if (!self.backgroundImageView) {
UIImageView *bgImageView = [UIImageView new];
bgImageView.contentMode = UIViewContentModeScaleAspectFit;
[self insertSubview:bgImageView belowSubview:self.mainView];
self.backgroundImageView = bgImageView;
}
self.backgroundImageView.image = placeholderImage;
}
- (void)setPageControlDotSize:(CGSize)pageControlDotSize
{
_pageControlDotSize = pageControlDotSize;
[self setupPageControl];
if ([self.pageControl isKindOfClass:[TAPageControl class]]) {
TAPageControl *pageContol = (TAPageControl *)_pageControl;
pageContol.dotSize = pageControlDotSize;
}
}
- (void)setShowPageControl:(BOOL)showPageControl
{
_showPageControl = showPageControl;
_pageControl.hidden = !showPageControl;
}
- (void)setCurrentPageDotColor:(UIColor *)currentPageDotColor
{
_currentPageDotColor = currentPageDotColor;
if ([self.pageControl isKindOfClass:[TAPageControl class]]) {
TAPageControl *pageControl = (TAPageControl *)_pageControl;
pageControl.dotColor = currentPageDotColor;
} else {
UIPageControl *pageControl = (UIPageControl *)_pageControl;
pageControl.currentPageIndicatorTintColor = currentPageDotColor;
}
}
- (void)setPageDotColor:(UIColor *)pageDotColor
{
_pageDotColor = pageDotColor;
if ([self.pageControl isKindOfClass:[UIPageControl class]]) {
UIPageControl *pageControl = (UIPageControl *)_pageControl;
pageControl.pageIndicatorTintColor = pageDotColor;
}
}
- (void)setCurrentPageDotImage:(UIImage *)currentPageDotImage
{
_currentPageDotImage = currentPageDotImage;
if (self.pageControlStyle != SDCycleScrollViewPageContolStyleAnimated) {
self.pageControlStyle = SDCycleScrollViewPageContolStyleAnimated;
}
[self setCustomPageControlDotImage:currentPageDotImage isCurrentPageDot:YES];
}
- (void)setPageDotImage:(UIImage *)pageDotImage
{
_pageDotImage = pageDotImage;
if (self.pageControlStyle != SDCycleScrollViewPageContolStyleAnimated) {
self.pageControlStyle = SDCycleScrollViewPageContolStyleAnimated;
}
[self setCustomPageControlDotImage:pageDotImage isCurrentPageDot:NO];
}
- (void)setCustomPageControlDotImage:(UIImage *)image isCurrentPageDot:(BOOL)isCurrentPageDot
{
if (!image || !self.pageControl) return;
if ([self.pageControl isKindOfClass:[TAPageControl class]]) {
TAPageControl *pageControl = (TAPageControl *)_pageControl;
if (isCurrentPageDot) {
pageControl.currentDotImage = image;
} else {
pageControl.dotImage = image;
}
}
}
- (void)setInfiniteLoop:(BOOL)infiniteLoop
{
_infiniteLoop = infiniteLoop;
if (self.imagePathsGroup.count) {
self.imagePathsGroup = self.imagePathsGroup;
}
}
-(void)setAutoScroll:(BOOL)autoScroll{
_autoScroll = autoScroll;
[self invalidateTimer];
if (_autoScroll) {
[self setupTimer];
}
}
- (void)setScrollDirection:(UICollectionViewScrollDirection)scrollDirection
{
_scrollDirection = scrollDirection;
_flowLayout.scrollDirection = scrollDirection;
}
- (void)setAutoScrollTimeInterval:(CGFloat)autoScrollTimeInterval
{
_autoScrollTimeInterval = autoScrollTimeInterval;
[self setAutoScroll:self.autoScroll];
}
- (void)setPageControlStyle:(SDCycleScrollViewPageContolStyle)pageControlStyle
{
_pageControlStyle = pageControlStyle;
[self setupPageControl];
}
- (void)setImagePathsGroup:(NSArray *)imagePathsGroup
{
[self invalidateTimer];
_imagePathsGroup = imagePathsGroup;
_totalItemsCount = self.infiniteLoop ? self.imagePathsGroup.count * 100 : self.imagePathsGroup.count;
if (imagePathsGroup.count > 1) { // !=1 count == 0
self.mainView.scrollEnabled = YES;
[self setAutoScroll:self.autoScroll];
} else {
self.mainView.scrollEnabled = NO;
[self invalidateTimer];
}
[self setupPageControl];
[self.mainView reloadData];
}
- (void)setImageURLStringsGroup:(NSArray *)imageURLStringsGroup
{
_imageURLStringsGroup = imageURLStringsGroup;
NSMutableArray *temp = [NSMutableArray new];
[_imageURLStringsGroup enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL * stop) {
NSString *urlString;
if ([obj isKindOfClass:[NSString class]]) {
urlString = obj;
} else if ([obj isKindOfClass:[NSURL class]]) {
NSURL *url = (NSURL *)obj;
urlString = [url absoluteString];
}
if (urlString) {
[temp addObject:urlString];
}
}];
self.imagePathsGroup = [temp copy];
}
- (void)setLocalizationImageNamesGroup:(NSArray *)localizationImageNamesGroup
{
_localizationImageNamesGroup = localizationImageNamesGroup;
self.imagePathsGroup = [localizationImageNamesGroup copy];
}
- (void)setTitlesGroup:(NSArray *)titlesGroup
{
_titlesGroup = titlesGroup;
if (self.onlyDisplayText) {
NSMutableArray *temp = [NSMutableArray new];
for (int i = 0; i < _titlesGroup.count; i++) {
[temp addObject:@""];
}
self.backgroundColor = [UIColor clearColor];
self.imageURLStringsGroup = [temp copy];
}
}
- (void)disableScrollGesture {
self.mainView.canCancelContentTouches = NO;
for (UIGestureRecognizer *gesture in self.mainView.gestureRecognizers) {
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
[self.mainView removeGestureRecognizer:gesture];
}
}
}
#pragma mark - actions
- (void)setupTimer
{
[self invalidateTimer]; //
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoScrollTimeInterval target:self selector:@selector(automaticScroll) userInfo:nil repeats:YES];
_timer = timer;
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)invalidateTimer
{
[_timer invalidate];
_timer = nil;
}
- (void)setupPageControl
{
if (_pageControl) [_pageControl removeFromSuperview]; //
if (self.imagePathsGroup.count == 0 || self.onlyDisplayText) return;
if ((self.imagePathsGroup.count == 1) && self.hidesForSinglePage) return;
int indexOnPageControl = [self pageControlIndexWithCurrentCellIndex:[self currentIndex]];
switch (self.pageControlStyle) {
case SDCycleScrollViewPageContolStyleAnimated:
{
TAPageControl *pageControl = [[TAPageControl alloc] init];
pageControl.numberOfPages = self.imagePathsGroup.count;
pageControl.dotColor = self.currentPageDotColor;
pageControl.userInteractionEnabled = NO;
pageControl.currentPage = indexOnPageControl;
[self addSubview:pageControl];
_pageControl = pageControl;
}
break;
case SDCycleScrollViewPageContolStyleClassic:
{
UIPageControl *pageControl = [[UIPageControl alloc] init];
pageControl.numberOfPages = self.imagePathsGroup.count;
pageControl.currentPageIndicatorTintColor = self.currentPageDotColor;
pageControl.pageIndicatorTintColor = self.pageDotColor;
pageControl.userInteractionEnabled = NO;
pageControl.currentPage = indexOnPageControl;
[self addSubview:pageControl];
_pageControl = pageControl;
}
break;
default:
break;
}
// pagecontroldot
if (self.currentPageDotImage) {
self.currentPageDotImage = self.currentPageDotImage;
}
if (self.pageDotImage) {
self.pageDotImage = self.pageDotImage;
}
}
- (void)automaticScroll
{
if (0 == _totalItemsCount) return;
int currentIndex = [self currentIndex];
int targetIndex = currentIndex + 1;
[self scrollToIndex:targetIndex];
}
- (void)scrollToIndex:(int)targetIndex
{
if (targetIndex >= _totalItemsCount) {
if (self.infiniteLoop) {
targetIndex = _totalItemsCount * 0.5;
[_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
}
return;
}
[_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}
- (int)currentIndex
{
if (_mainView.sd_width == 0 || _mainView.sd_height == 0) {
return 0;
}
int index = 0;
if (_flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) {
index = (_mainView.contentOffset.x + _flowLayout.itemSize.width * 0.5) / _flowLayout.itemSize.width;
} else {
index = (_mainView.contentOffset.y + _flowLayout.itemSize.height * 0.5) / _flowLayout.itemSize.height;
}
return MAX(0, index);
}
- (int)pageControlIndexWithCurrentCellIndex:(NSInteger)index
{
return (int)index % self.imagePathsGroup.count;
}
- (void)clearCache
{
[[self class] clearImagesCache];
}
+ (void)clearImagesCache
{
[[[SDWebImageManager sharedManager] imageCache] clearWithCacheType:SDImageCacheTypeDisk completion:nil];
}
#pragma mark - life circles
- (void)layoutSubviews
{
self.delegate = self.delegate;
[super layoutSubviews];
_flowLayout.itemSize = self.frame.size;
_mainView.frame = self.bounds;
if (_mainView.contentOffset.x == 0 && _totalItemsCount) {
int targetIndex = 0;
if (self.infiniteLoop) {
targetIndex = _totalItemsCount * 0.5;
}else{
targetIndex = 0;
}
[_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
}
CGSize size = CGSizeZero;
if ([self.pageControl isKindOfClass:[TAPageControl class]]) {
TAPageControl *pageControl = (TAPageControl *)_pageControl;
if (!(self.pageDotImage && self.currentPageDotImage && CGSizeEqualToSize(kCycleScrollViewInitialPageControlDotSize, self.pageControlDotSize))) {
pageControl.dotSize = self.pageControlDotSize;
}
size = [pageControl sizeForNumberOfPages:self.imagePathsGroup.count];
} else {
size = CGSizeMake(self.imagePathsGroup.count * self.pageControlDotSize.width * 1.5, self.pageControlDotSize.height);
// ios14 pageControl size
if (@available(iOS 14.0, *)) {
if ([self.pageControl isKindOfClass:[UIPageControl class]]) {
UIPageControl *pageControl = (UIPageControl *)_pageControl;
size.width = [pageControl sizeForNumberOfPages:self.imagePathsGroup.count].width;
}
}
}
CGFloat x = (self.sd_width - size.width) * 0.5;
if (self.pageControlAliment == SDCycleScrollViewPageContolAlimentRight) {
x = self.mainView.sd_width - size.width - 10;
}
CGFloat y = self.mainView.sd_height - size.height - 10;
if ([self.pageControl isKindOfClass:[TAPageControl class]]) {
TAPageControl *pageControl = (TAPageControl *)_pageControl;
[pageControl sizeToFit];
}
CGRect pageControlFrame = CGRectMake(x, y, size.width, size.height);
pageControlFrame.origin.y -= self.pageControlBottomOffset;
pageControlFrame.origin.x -= self.pageControlRightOffset;
self.pageControl.frame = pageControlFrame;
self.pageControl.hidden = !_showPageControl;
if (self.backgroundImageView) {
self.backgroundImageView.frame = self.bounds;
}
}
//ViewTimer
- (void)willMoveToSuperview:(UIView *)newSuperview
{
if (!newSuperview) {
[self invalidateTimer];
}
}
//timer scrollViewDidScroll访
- (void)dealloc {
_mainView.delegate = nil;
_mainView.dataSource = nil;
}
#pragma mark - public actions
- (void)adjustWhenControllerViewWillAppera
{
long targetIndex = [self currentIndex];
if (targetIndex < _totalItemsCount) {
[_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
}
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _totalItemsCount;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
SDCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
long itemIndex = [self pageControlIndexWithCurrentCellIndex:indexPath.item];
if ([self.delegate respondsToSelector:@selector(setupCustomCell:forIndex:cycleScrollView:)] &&
[self.delegate respondsToSelector:@selector(customCollectionViewCellClassForCycleScrollView:)] && [self.delegate customCollectionViewCellClassForCycleScrollView:self]) {
[self.delegate setupCustomCell:cell forIndex:itemIndex cycleScrollView:self];
return cell;
}else if ([self.delegate respondsToSelector:@selector(setupCustomCell:forIndex:cycleScrollView:)] &&
[self.delegate respondsToSelector:@selector(customCollectionViewCellNibForCycleScrollView:)] && [self.delegate customCollectionViewCellNibForCycleScrollView:self]) {
[self.delegate setupCustomCell:cell forIndex:itemIndex cycleScrollView:self];
return cell;
}
NSString *imagePath = self.imagePathsGroup[itemIndex];
if (!self.onlyDisplayText && [imagePath isKindOfClass:[NSString class]]) {
if ([imagePath hasPrefix:@"http"]) {
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:imagePath] placeholderImage:self.placeholderImage];
} else {
UIImage *image = [UIImage imageNamed:imagePath];
if (!image) {
image = [UIImage imageWithContentsOfFile:imagePath];
}
cell.imageView.image = image;
}
} else if (!self.onlyDisplayText && [imagePath isKindOfClass:[UIImage class]]) {
cell.imageView.image = (UIImage *)imagePath;
}
if (_titlesGroup.count && itemIndex < _titlesGroup.count) {
cell.title = _titlesGroup[itemIndex];
}
if (!cell.hasConfigured) {
cell.titleLabelBackgroundColor = self.titleLabelBackgroundColor;
cell.titleLabelHeight = self.titleLabelHeight;
cell.titleLabelTextAlignment = self.titleLabelTextAlignment;
cell.titleLabelTextColor = self.titleLabelTextColor;
cell.titleLabelTextFont = self.titleLabelTextFont;
cell.hasConfigured = YES;
cell.imageView.contentMode = self.bannerImageViewContentMode;
cell.clipsToBounds = YES;
cell.onlyDisplayText = self.onlyDisplayText;
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didSelectItemAtIndex:)]) {
[self.delegate cycleScrollView:self didSelectItemAtIndex:[self pageControlIndexWithCurrentCellIndex:indexPath.item]];
}
if (self.clickItemOperationBlock) {
self.clickItemOperationBlock([self pageControlIndexWithCurrentCellIndex:indexPath.item]);
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (!self.imagePathsGroup.count) return; // timer
int itemIndex = [self currentIndex];
int indexOnPageControl = [self pageControlIndexWithCurrentCellIndex:itemIndex];
if ([self.pageControl isKindOfClass:[TAPageControl class]]) {
TAPageControl *pageControl = (TAPageControl *)_pageControl;
pageControl.currentPage = indexOnPageControl;
} else {
UIPageControl *pageControl = (UIPageControl *)_pageControl;
pageControl.currentPage = indexOnPageControl;
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (self.autoScroll) {
[self invalidateTimer];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (self.autoScroll) {
[self setupTimer];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self scrollViewDidEndScrollingAnimation:self.mainView];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
if (!self.imagePathsGroup.count) return; // timer
int itemIndex = [self currentIndex];
int indexOnPageControl = [self pageControlIndexWithCurrentCellIndex:itemIndex];
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didScrollToIndex:)]) {
[self.delegate cycleScrollView:self didScrollToIndex:indexOnPageControl];
} else if (self.itemDidScrollOperationBlock) {
self.itemDidScrollOperationBlock(indexOnPageControl);
}
}
- (void)makeScrollViewScrollToIndex:(NSInteger)index{
if (self.autoScroll) {
[self invalidateTimer];
}
if (0 == _totalItemsCount) return;
[self scrollToIndex:(int)(_totalItemsCount * 0.5 + index)];
if (self.autoScroll) {
[self setupTimer];
}
}
@end