Recently, I have been looking for a method to realize picture infinite rotation, and I have seen a lot of methods on the Internet, but most of them are not quite suitable. Finally, I saw a lecturer of an IT training company using UICollectionView: a line of code to achieve picture infinite rotation. Of course, I want to realize the function of rotation with a line of code, and there are still some work to do in the early stage. Here’s how to share it with you!

I. Effect drawing of picture infinite rotation:




Picture infinite rotation. GIF

Ii. Implementation Principle and Analysis:

Assuming there are three pictures 0, 1 and 2, we can set the cell number of UICollectionView as the number of pictures x 3 to realize infinite rotation, that is, add three pictures to nine cells repeatedly, and infinite rotation can be decomposed into five special states (five critical points). The initial state is at the beginning of rotation. Under the function of the timer, the second image is displayed in the right critical state. If we want to seamlessly scroll to the 0 image, we can secretly scroll the collectionView to the third cell, you can see the fourth transition diagram, the second image is still displayed. Then the next scroll will be the 0 image, which will realize the infinite loop of cell scroll to the left; Scrolling to the right works the same way, from the third image to the fifth.




Initial boundary status.png




Right criticality. PNG




Left criticality. PNG




Paste_Image.png




Paste_Image.png

Iii. Code:




Class files. PNG

  • JFWeakTimerTargetObject inherits from NSObject
  • JFLoopView inherits from UIView
  • JFLoopViewCell inherits from UICollectionViewCell
  • JFLoopViewLayout inherited from UICollectionViewFlowLayout
  • JFMainViewController inherits from UIViewController

JFWeakTimerTargetObject rewrite timer NSTimer + (NSTimer *) scheduledTimerWithTimeInterval aTarget: NSTimeInterval ti target: (id) selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo; The purpose of the class method is to avoid the problem of JFLoopView not being released when the timer strongly references the class. JFWeakTimerTargetObject. H file

#import 

@interface JFWeakTimerTargetObject : NSObject

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

@endCopy the code

JFWeakTimerTargetObject. M file

#import "JFWeakTimerTargetObject.h" @interface JFWeakTimerTargetObject () @property (nonatomic, weak) id target; @property (nonatomic, assign) SEL selector; @end @implementation JFWeakTimerTargetObject + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti Target (id)aTarget selector (SEL)aSelector userInfo (nullable id)userInfo repeats:(BOOL)yesOrNo {// create the current class object JFWeakTimerTargetObject *object = [[JFWeakTimerTargetObject alloc] init]; object.target = aTarget; object.selector = aSelector; return [NSTimer scheduledTimerWithTimeInterval:ti target:object selector:@selector(fire:) userInfo:userInfo repeats:yesOrNo]; } - (void)fire:(id)obj { [self.target performSelector:self.selector withObject:obj]; } @endCopy the code

JFLoopView. H file

#import @interface JFLoopView: UIView - (InstanceType)initWithImageArray:(NSArray *)imageArray; @endCopy the code

JFLoopView. M file

#import "JFLoopView.h" #import "JFLoopViewLayout.h" #import "JFLoopViewCell.h" #import "JFWeakTimerTargetObject.h" @interface JFLoopView () @property (nonatomic, strong) UICollectionView *collectionView; @property (nonatomic, strong) UIPageControl *pageControl; @property (nonatomic, strong) NSArray *imageArray; @property (nonatomic, weak) NSTimer *timer; @end static NSString *ID = @"loopViewCell"; @implementation JFLoopView - (instancetype)initWithImageArray:(NSArray *)imageArray { if (self = [super init]) { UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:[[JFLoopViewLayout alloc] init]]; [collectionView registerClass:[JFLoopViewCell class] forCellWithReuseIdentifier:ID]; collectionView.dataSource = self; collectionView.delegate = self; [self addSubview:collectionView]; self.collectionView = collectionView; self.imageArray = imageArray; / / add the pager [self addSubview: self. The pageControl]; Dispatch_async (dispatch_get_main_queue()), ^ {/ / set the initial state roll in [self. CollectionView scrollToItemAtIndexPath: [NSIndexPath indexPathForItem: self. ImageArray. Count inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO]; // addTimer [self addTimer]; }); } return self; } pageControl - (UIPageControl *)pageControl {if (! _pageControl) { _pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 220, 0, 30)]; _pageControl.numberOfPages = self.imageArray.count; _pageControl.pageIndicatorTintColor = [UIColor orangeColor]; _pageControl.currentPageIndicatorTintColor = [UIColor purpleColor]; } return _pageControl; } # pragma mark - UICollectionViewDataSource data source method (NSInteger) collectionView: (collectionView UICollectionView *) numberOfItemsInSection:(NSInteger)section { return self.imageArray.count * 3; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { JFLoopViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath]; cell.imageName = self.imageArray[indexPath.item % self.imageArray.count]; return cell; } #pragma mark ---- UICollectionViewDelegate Will call this method) - (void) scrollViewDidEndScrollingAnimation: (UIScrollView *) scrollView {[self scrollViewDidEndDecelerating:scrollView]; } / / / when rolling reduction calls - (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView {CGFloat offsetX = scrollView.contentOffset.x; NSInteger page = offsetX / scrollView.bounds.size.width; If (page == 0) {page = self.imagearray.count; self.collectionView.contentOffset = CGPointMake(page * scrollView.frame.size.width, 0); Else if (page == [self.collectionView numberOfItemsInSection:0] -1) {page = self.imagearray.count -1; self.collectionView.contentOffset = CGPointMake(page * scrollView.frame.size.width, 0); } // set UIPageControl currentPage NSInteger currentPage = page % self.imagearray.count; self.pageControl.currentPage =currentPage; // addTimer [self addTimer]; } / / / fingers began to drag when the - (void) scrollViewWillBeginDragging: (scrollView UIScrollView *) {/ / removing the timer [self removeTimer]; } // addTimer - (void)addTimer {if (self.timer) return; The self. The timer = [JFWeakTimerTargetObject scheduledTimerWithTimeInterval: 1.5 target: self selector: @ the selector (nextImage) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; } // removeTimer - (void)removeTimer {[self.timer invalidate]; self.timer = nil; } / / / switch to the next image - (void) nextImage {CGFloat offsetX = self. CollectionView. ContentOffset. X. NSInteger page = offsetX / self.collectionView.bounds.size.width; [self.collectionView setContentOffset:CGPointMake((page + 1) * self.collectionView.bounds.size.width, 0) animated:YES]; } - (void)layoutSubviews { [super layoutSubviews]; self.collectionView.frame = self.bounds; } - (void)dealloc { [self removeTimer]; } @endCopy the code

JFLoopViewCell. H file

#import 

@interface JFLoopViewCell : UICollectionViewCell

@property (nonatomic, copy) NSString *imageName;

@endCopy the code

JFLoopViewCell. M file

#import "JFLoopViewCell.h"

@interface JFLoopViewCell ()

@property (nonatomic, weak) UIImageView *iconView;

@end

@implementation JFLoopViewCell

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        UIImageView *iconView = [[UIImageView alloc] init];
        [self addSubview:iconView];
        self.iconView = iconView;
    }
    return self;
}

- (void)setImageName:(NSString *)imageName {
    _imageName = imageName;
    self.iconView.image = [UIImage imageNamed:imageName];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.iconView.frame = self.bounds;
}

@endCopy the code

JFLoopViewLayout. H file

#import 

@interface JFLoopViewLayout : UICollectionViewFlowLayout

@endCopy the code

JFLoopViewLayout. M file

#import "jfloopViewLayout. h" @implementation JFLoopViewLayout - (void)prepareLayout {[super prepareLayout]; Self. / / set the item size itemSize = self. CollectionView. Frame. The size; / / set the scroll direction self. ScrollDirection = UICollectionViewScrollDirectionHorizontal; / / set the paging self. CollectionView. PagingEnabled = YES; // Set the minimum space self.minimumLineSpacing = 0; self.minimumInteritemSpacing = 0; / / hide the horizontal scroll bar self. CollectionView. ShowsHorizontalScrollIndicator = NO. } @endCopy the code

JFMainViewController. H file

#import 

@interface JFMainViewController : UIViewController

@endCopy the code

JFMainViewController. M file

#import "JFMainViewController.h" #import "JFLoopView.h" @interface JFMainViewController () @property (nonatomic, strong) JFLoopView *loopView; @end @implementation JFMainViewController - (void)viewDidLoad { [super viewDidLoad]; / / close automatically adjust the scroll view self. AutomaticallyAdjustsScrollViewInsets = NO; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.navigationController.navigationBar.hidden = YES; } - (void)loadView { [super loadView]; NSArray *imageArray = @[@" srCOLL_01 ",@"srcoll_02",@"srcoll_03"]; Code / / visit infinite shuffling _loopView = [[JFLoopView alloc] initWithImageArray: imageArray]; // Set loopView's frame _loopView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 250); [self.view addSubview:self.loopView]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @endCopy the code

Note: if you have UINavigationBar controller, and hid the navigationBar, must remember setting self. AutomaticallyAdjustsScrollViewInsets = NO; AutomaticallyAdjustsScrollViewInsets is why? Simply means automaticallyAdjustsScrollViewInsets points according to the interface of the status bar, navigationbar, and the height of the tabbar, automatically adjust the scrollview an inset, set to NO, We don’t let the viewController adjust it, we just change the layout ourselves. If you do not set it to NO, the imageView position will change when you automatically scroll and drag.




Picture infinite rotation bug display. GIF

Iv. Summary:

1. The main control of the infinite unicast is UICollectionView and UIPageControl. 2, good encapsulation tool class later when using a line _loopView = [[JFLoopView alloc] initWithImageArray: imageArray]; Code, and then set frame to reuse the infinite caster. 3, a reasonable way to switch pictures and picture arrangement, coupled with the appropriate use of UICollectionView provided by the proxy method can be perfect to achieve the infinite wheel.

Write at the end:

The next article will talk about how to use UICollectionView to realize the homepage of e-commerce APP:




The home page of e-commerce APP shows.gif

If you have a good idea please share, thanks for reading! Welcome to follow and comment!