JXBannerView.m 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. //
  2. // JXBannerView.m
  3. // AICity
  4. //
  5. // Banner 轮播组件实现
  6. //
  7. #import "JXBannerView.h"
  8. #import <SDWebImage/SDWebImage.h>
  9. #import <Masonry/Masonry.h>
  10. static const NSTimeInterval kAutoScrollInterval = 5.0; // 5秒自动滚动
  11. static NSString * const kBannerCellIdentifier = @"BannerCell";
  12. @interface JXBannerCell : UICollectionViewCell
  13. @property (nonatomic, strong) UIImageView *imageView;
  14. @property (nonatomic, strong) UILabel *titleLabel;
  15. @end
  16. @implementation JXBannerCell
  17. - (instancetype)initWithFrame:(CGRect)frame {
  18. self = [super initWithFrame:frame];
  19. if (self) {
  20. self.imageView = [[UIImageView alloc] init];
  21. self.imageView.contentMode = UIViewContentModeScaleAspectFill;
  22. self.imageView.clipsToBounds = YES;
  23. [self.contentView addSubview:self.imageView];
  24. self.titleLabel = [[UILabel alloc] init];
  25. self.titleLabel.textColor = [UIColor whiteColor];
  26. self.titleLabel.font = [UIFont boldSystemFontOfSize:16];
  27. self.titleLabel.numberOfLines = 2;
  28. self.titleLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
  29. [self.contentView addSubview:self.titleLabel];
  30. [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
  31. make.edges.equalTo(self.contentView);
  32. }];
  33. [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  34. make.left.right.bottom.equalTo(self.contentView);
  35. make.height.mas_equalTo(60);
  36. }];
  37. }
  38. return self;
  39. }
  40. @end
  41. #pragma mark - Banner View
  42. @interface JXBannerView () <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
  43. @property (nonatomic, strong) UICollectionView *collectionView;
  44. @property (nonatomic, strong) UIPageControl *pageControl;
  45. @property (nonatomic, strong) NSTimer *autoScrollTimer;
  46. @property (nonatomic, assign) NSInteger currentIndex;
  47. @end
  48. @implementation JXBannerView
  49. - (instancetype)initWithFrame:(CGRect)frame {
  50. self = [super initWithFrame:frame];
  51. if (self) {
  52. [self setupUI];
  53. }
  54. return self;
  55. }
  56. - (void)dealloc {
  57. [self stopAutoScroll];
  58. }
  59. - (void)setupUI {
  60. // CollectionView Layout
  61. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
  62. layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
  63. layout.minimumLineSpacing = 0;
  64. layout.minimumInteritemSpacing = 0;
  65. // CollectionView
  66. self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
  67. self.collectionView.delegate = self;
  68. self.collectionView.dataSource = self;
  69. self.collectionView.pagingEnabled = YES;
  70. self.collectionView.showsHorizontalScrollIndicator = NO;
  71. self.collectionView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
  72. [self.collectionView registerClass:[JXBannerCell class] forCellWithReuseIdentifier:kBannerCellIdentifier];
  73. [self addSubview:self.collectionView];
  74. // PageControl
  75. self.pageControl = [[UIPageControl alloc] init];
  76. self.pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];
  77. self.pageControl.pageIndicatorTintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5];
  78. self.pageControl.userInteractionEnabled = NO;
  79. [self addSubview:self.pageControl];
  80. // Layout
  81. [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
  82. make.edges.equalTo(self);
  83. }];
  84. [self.pageControl mas_makeConstraints:^(MASConstraintMaker *make) {
  85. make.centerX.equalTo(self);
  86. make.bottom.equalTo(self).offset(-10);
  87. make.height.mas_equalTo(20);
  88. }];
  89. }
  90. - (void)updateBanners:(NSArray<JXBannerItem *> *)banners {
  91. self.banners = banners;
  92. self.currentIndex = 0;
  93. // 更新 PageControl
  94. self.pageControl.numberOfPages = banners.count;
  95. self.pageControl.currentPage = 0;
  96. self.pageControl.hidden = (banners.count <= 1);
  97. // 刷新数据
  98. [self.collectionView reloadData];
  99. // 如果有多个 Banner,启动自动轮播
  100. if (banners.count > 1) {
  101. [self startAutoScroll];
  102. }
  103. }
  104. #pragma mark - Auto Scroll
  105. - (void)startAutoScroll {
  106. [self stopAutoScroll];
  107. if (self.banners.count > 1) {
  108. self.autoScrollTimer = [NSTimer scheduledTimerWithTimeInterval:kAutoScrollInterval
  109. target:self
  110. selector:@selector(autoScroll)
  111. userInfo:nil
  112. repeats:YES];
  113. }
  114. }
  115. - (void)stopAutoScroll {
  116. if (self.autoScrollTimer) {
  117. [self.autoScrollTimer invalidate];
  118. self.autoScrollTimer = nil;
  119. }
  120. }
  121. - (void)autoScroll {
  122. if (self.banners.count == 0) {
  123. return;
  124. }
  125. NSInteger nextIndex = (self.currentIndex + 1) % self.banners.count;
  126. NSIndexPath *indexPath = [NSIndexPath indexPathForItem:nextIndex inSection:0];
  127. [self.collectionView scrollToItemAtIndexPath:indexPath
  128. atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
  129. animated:YES];
  130. }
  131. #pragma mark - UICollectionViewDataSource
  132. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
  133. return self.banners.count;
  134. }
  135. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  136. JXBannerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kBannerCellIdentifier forIndexPath:indexPath];
  137. JXBannerItem *item = self.banners[indexPath.item];
  138. // 使用横向封面
  139. NSString *imageUrl = item.hCoverUrl ?: item.vCoverUrl;
  140. if (imageUrl) {
  141. [cell.imageView sd_setImageWithURL:[NSURL URLWithString:imageUrl]
  142. placeholderImage:nil];
  143. }
  144. cell.titleLabel.text = item.name;
  145. return cell;
  146. }
  147. #pragma mark - UICollectionViewDelegate
  148. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  149. JXBannerItem *item = self.banners[indexPath.item];
  150. if (self.onBannerClick) {
  151. self.onBannerClick(item);
  152. }
  153. }
  154. #pragma mark - UICollectionViewDelegateFlowLayout
  155. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
  156. return collectionView.bounds.size;
  157. }
  158. #pragma mark - UIScrollViewDelegate
  159. - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  160. CGFloat pageWidth = scrollView.frame.size.width;
  161. NSInteger page = (NSInteger)((scrollView.contentOffset.x + pageWidth / 2) / pageWidth);
  162. if (page >= 0 && page < self.banners.count) {
  163. self.currentIndex = page;
  164. self.pageControl.currentPage = page;
  165. }
  166. }
  167. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  168. // 用户开始拖动时停止自动滚动
  169. [self stopAutoScroll];
  170. }
  171. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
  172. // 用户停止拖动后重新开始自动滚动
  173. if (!decelerate) {
  174. [self startAutoScroll];
  175. }
  176. }
  177. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
  178. // 减速停止后重新开始自动滚动
  179. [self startAutoScroll];
  180. }
  181. @end