CollectionView implementation, N+2 imageView implementation, three imageView implementation, two imageView implementation. The collectionView is a little lazy. N+2 is more resource efficient than 3 imageViews. The two imageViews are not known yet, so I won’t introduce them here.
Two, about the reference of the owner of the building in writing round broadcast when checked a lot of information, there are a lot of good third party library, but feel this simple function or do a little bit of their own, later maintenance is also convenient, the following will also refer to the article link posted out. There are some holes in the following article, I mainly refer to some structure and naming. Refer to the article: http://www.jianshu.com/p/1325998d976b?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=qq
A UIView contains two subviews: UIScrollView and UIPageControl. And then finally when you’re going to take the wheel map out and use it you just use this UIView object. Q1: Why not put UIPageControl directly in A ScrollView: If you put pageContro in a ScrollView, the dots will scroll as you scroll through the scrolling graph, and you need to add a pageControl on each graph, which is visually awkward and cumbersome. If you don’t then you need to dynamically calculate and set pageControl’s frame, which I think is a lot more resource-heavy than the approach I took.
In this article, the author has not found any pits, but there are two uncertain points, these two points may have pits in actual use, I hope you pay attention to the details as follows: 1. KVO was used in the code, but it was removed from the dealloc of the relevant view when removing KVO. I am not sure if there is a problem with this place, if there is a big god, I hope to correct it. 2, NSTimer (timer) should be started and removed in ViewWillAppera and viewDidDisappear respectively, to prevent cyclic references and unable to release objects, but it is found that the two methods are not called in View. The only way to call these two methods is in the Controller. So the last one is removed in dealloc, I don’t know if it’s going to leave a pit, so I want you to pay attention to that. If there is a god, can help me answer, thank you.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — I’m a text line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
I don’t want to say more about it. Since there are three images, during the implementation process, in order to slide left and right, there are all images, so the initialization, the offset of the UIScrollView should be its own width, and whether it is the last image or the next image, in order to slide left and right, at the end of the slide, The UIScrollView offset is set to its own width, so after the UIScrollView slides through an image, the UIScrollView offset is set to its own width.
Concrete implementation: create two files: ZWCHeaderViewScrollView. H ZWCHeaderViewScrollView. M ZWCheaderView h ZWCheaderView. M
In the first ZWCHeaderViewScrollView. H to external interface:
@Property (nonatomic,assign)NSInteger currentPageInteger; // @property(nonatomic,strong)NSTimer *rotateTimer; // override init method - (instancetype)initWithFrame:(CGRect)frame; // Publish an external method, passing in the image's network address Settings to show the wheel map. - (void)showWith:(NSArray *)imageViewUrlArray;Copy the code
ZWCHeaderViewScrollView. M in three imageView needs to create the fact
imageView@property (nonatomic,strong)UIImageView *leftImageView; @property(nonatomic,strong)UIImageView *centerImageView; @property(nonatomic,strong)UIImageView *rightImageView;Copy the code
And declare the following attributes:
@property(nonatomic,strong)NSArray *imageArray; // Save the frame passed in. @property(nonatomic,assign)CGRect scrViewFrame;Copy the code
Lazy loading of three properties
#pragma mark -- Attribute lazy loading
-(UIImageView *)leftImageView{
if (_leftImageView == nil) {
_leftImageView = [[UIImageView alloc]init];
}
return _leftImageView;
}
-(UIImageView *)centerImageView{
if (_centerImageView == nil) {
_centerImageView = [[UIImageView alloc]init];
}
return _centerImageView;
}
-(UIImageView *)rightImageView{
if (_rightImageView == nil) {
_rightImageView = [[UIImageView alloc]init];
}
return _rightImageView;
}
Copy the code
Then, to get the idea straight: ZWCHeaderViewScrollView (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView) (ZWCHeaderViewScrollView)
According to the above ideas ZWCHeaderViewScrollView. M in the following code
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self) { self.scrViewFrame = frame; self.contentSize = CGSizeMake(CGRectGetWidth(frame)*3, CGRectGetHeight(frame)); // Make sure the offset is set to show the middle image. self.contentOffset = CGPointMake(CGRectGetWidth(frame), CGRectGetMinY(frame)); self.delegate = self; self.pagingEnabled = YES; self.showsHorizontalScrollIndicator = NO; Self.currentpageinteger = 0; self.currentPageInteger = 0; }returnself; } -(void)showWith:(NSArray *)imageViewUrlArray { self.imageArray = imageViewUrlArray; // Create and add three views [self creaetLeftImageView]; [self createRightImageView]; [self createCenterImageView]; } // Left view - (void)creaetLeftImageView{NSInteger index = (self.currentPageInteger-1+self.imageArray.count)%self.imageArray.count; self.leftImageView.frame =CGRectMake(0, 0, CGRectGetWidth(self.scrViewFrame), CGRectGetHeight(self.scrViewFrame)); [self.leftImageView sd_setImageWithURL:self.imageArray[index][@"pic"] placeholderImage:[UIImage imageNamed:@"tempImage"]]. self.leftImageView.contentMode = UIViewContentModeScaleAspectFill; self.leftImageView.userInteractionEnabled = YES; [self addSubview:self.leftImageView]; } / / middle view - (void) createCenterImageView {self. CenterImageView. Frame = CGRectMake (CGRectGetWidth (self. ScrViewFrame), 0. CGRectGetWidth(self.scrViewFrame), CGRectGetHeight(self.scrViewFrame)); [self.centerImageView sd_setImageWithURL:self.imageArray[self.currentPageInteger][@"pic"] placeholderImage:[UIImage imageNamed:@"tempImage"]]. self.centerImageView.contentMode = UIViewContentModeScaleAspectFill; self.centerImageView.userInteractionEnabled = YES; [self addSubview:self.centerImageView]; } / / right view - (void) createRightImageView {self. RightImageView. Frame = CGRectMake (CGRectGetWidth (self. ScrViewFrame) * 2, 0, CGRectGetWidth(self.scrViewFrame), CGRectGetHeight(self.scrViewFrame)); NSInteger index = (self.currentPageInteger+1+self.imageArray.count)%self.imageArray.count; [self.rightImageView sd_setImageWithURL:self.imageArray[index][@"pic"] placeholderImage:[UIImage imageNamed:@"tempImage"]];
self.rightImageView.contentMode = UIViewContentModeScaleAspectFill;
self.rightImageView.userInteractionEnabled = YES;
[self addSubview:self.rightImageView];
}
Copy the code
We use SDWebImage directly because all the images we use are network images. If you are using local images, the array here is just the name of the map. At this point, I’ve added three images and I’m ready to scroll, but only to scroll left and right. You can’t scroll indefinitely, and then think again: look at the picture
As soon as the user has finished dragging the screen, we need to determine whether the user has turned the page (ScrollView does not turn the page when the user has not dragged more than half of the screen distance), left or right, and set the corresponding image to display according to the left or right.
The following method (ScrollView proxy method) is called every time the ScrollView slide stops to capture the moment when the user is done dragging the image data.
// This method is called when scrolling stops scrolling. - (void) scrollViewDidEndDecelerating: (scrollView UIScrollView *) {/ / because if not flip success, contentOffSet will return to the origin, so there only need to judgment can be greater than zero is less than 0. // Get how far the scroll view moves. CGFloat userDistance = self.contentOffset.x - CGRectGetWidth(self.scrViewFrame);if(userDistance < 0) {// Scroll left, Self.currentpageinteger = (self.currentPageInteger -1 + self.imagearRay.count)% self.imagearRay.count; [self resetContent]; }else if(userDistance > 0){// Scroll right, Self.currentpageinteger = (self.currentPageInteger + 1 + self.imagearRay.count)% self.imagearRay.count; [self resetContent]; }else{// The user does nothing until the page is turned successfully. }}Copy the code
Immediately after changing the display image data, call the reset data method:
#pragma mark ---- Resets data after users turn pages- (void)resetContent{// Reset the offset CGPoint offset = CGPointMake(CGRectGetWidth(self.scrViewFrame), 0); [selfsetContentOffset:offset]; NSInteger leftIndex = (self.currentPageINTEGER -1+ self.imagearRay.count)% self.imagearRay.count; NSInteger centerIndex = self.currentPageInteger; NSInteger rightIndex = (self.currentPageInteger+1+self.imageArray.count)%self.imageArray.count; [self.leftImageView sd_setImageWithURL:self.imageArray[leftIndex][@"pic"] placeholderImage:[UIImage imageNamed:@"tempImage"]];
[self.centerImageView sd_setImageWithURL:self.imageArray[centerIndex][@"pic"] placeholderImage:[UIImage imageNamed:@"tempImage"]];
[self.rightImageView sd_setImageWithURL:self.imageArray[rightIndex][@"pic"] placeholderImage:[UIImage imageNamed:@"tempImage"]];
}
Copy the code
At this point, your scroll view is ready for infinite rotation. The next thing to do is to add a timer to realize automatic rotation.
Some basic knowledge about the timer does not understand can look here: www.cnblogs.com/zhang6332/p…
Start the timer in the init method, just one line of code.
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self) { self.scrViewFrame = frame; self.contentSize = CGSizeMake(CGRectGetWidth(frame)*3, CGRectGetHeight(frame)); // Make sure the offset is set to show the middle image. self.contentOffset = CGPointMake(CGRectGetWidth(frame), CGRectGetMinY(frame)); self.delegate = self; self.pagingEnabled = YES; self.showsHorizontalScrollIndicator = NO; Self.currentpageinteger = 0; self.currentPageInteger = 0; / / start the self timer. RotateTimer = [NSTimer scheduledTimerWithTimeInterval: 4 target: self selector: @ the selector (aoutScroll) userInfo:nil repeats:YES]; }return self;
}
Copy the code
After the timer is started, the aoutScroll method is called as follows: Note that animation is required when the timer automatically turns pages.
Pragma mark - The timer automatically turns the page
- (void)aoutScroll{
NSLog(@"Timer called ------------------------"); // If the user is dragging the screen or the view is scrolling, the page cannot be turned automatically to avoid conflicts with user operations.if(! [self isDragging] || ! [self isactivity]) {// Just contentOffset, Because once set the contentOffSet agent will automatically call the - (void) scrollViewDidEndDecelerating: (scrollView UIScrollView *) this method, code reuse, Will use the logic we wrote up to help us deal with the rest of the stuff. [selfsetContentOffset:CGPointMake(CGRectGetWidth(self.scrViewFrame)*2, 0) animated:YES]; }}Copy the code
After adding a timer, you need to consider that if the user is dragging the view or the view is scrolling, you need to pause the timer at this point, and then start the timer after the user is dragging the view. Prevent action conflicts with users.
Pragma mark -- ScrollView agent/ / when the contentoffset method calls at the animation time - (void) scrollViewDidEndScrollingAnimation: (UIScrollView *) scrollView {[self scrollViewDidEndDecelerating:self]; NSLog(@"Call method contentoffset method call method +++++++++++ when animation is complete"); } / / when the finger drag call time method - (void) scrollViewWillBeginDragging (scrollView UIScrollView *) {NSLog (@"Dragging view, so I need to pause autoplay.");
//set//[NSDate distantFuture]: a certain time in the future [self.rotateTimersetFireDate:[NSDate distantFuture]];
}
Copy the code
Start the timer when the user screen stops scrolling. (Note the notes)
// This method is called when finger-drag scrolling stops. - (void) scrollViewDidEndDecelerating: (scrollView UIScrollView *) {/ / view static, 4 seconds in open timer NSLog (@"Turn on the timer");
[self.rotateTimer setFireDate:[NSDate dateWithTimeInterval:4 sinceDate:[NSDate date]]];
NSLog(@"scrollViewDidEndDecelerating------------%f",self.contentOffset.x); // Get how far the scroll view moves. CGFloat userDistance = self.contentOffset.x - CGRectGetWidth(self.scrViewFrame);if(userDistance < 0) {// Scroll left, Self.currentpageinteger = (self.currentPageInteger -1 + self.imagearRay.count)% self.imagearRay.count; [self resetContent]; }else if(userDistance > 0){// Scroll right, Self.currentpageinteger = (self.currentPageInteger + 1 + self.imagearRay.count)% self.imagearRay.count; [self resetContent]; }else{// The user does nothing until the page is turned successfully. }}Copy the code
So at this point, you can automatically scroll indefinitely. Next you need to add pageControl
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — I’m line — — — — — — — — — — — — — — — — — new ZWCheaderView. H ZWCheaderView. M
Also expose the same two methods in zwcheaderView.h
- (instancetype)initWithFrame:(CGRect)frame;
- (void)showWith:(NSArray *)imageViewUrlArray;
Copy the code
Declare two properties in zwcheaderView.m
@property(nonatomic,strong)ZWCHeaderViewScrollView *bannerScrView; @property(nonatomic,strong)UIPageControl *bannerpageControl;Copy the code
BannerpageControl lazy loading
-(UIPageControl *)bannerpageControl{
if (_bannerpageControl == nil) {
_bannerpageControl = [[UIPageControl alloc]initWithFrame:CGRectMake(CGRectGetWidth(self.frame)-80, CGRectGetHeight(self.frame)-30, 40, 20)];
}
return _bannerpageControl;
}
Copy the code
When the currentPage of the ScrollView changes to indicate that the ScrollView has turned the page, use KVO to set the dot to the corresponding position.
The following code I think small white should understand. 2. Add pageControl to this View. 3. 4, eliminate KVO, eliminate timer.
Override the init method and set some properties
-(instancetype)initWithFrame:(CGRect)frame{
if(self = [super initWithFrame:frame]) { self.bannerScrView = [[ZWCHeaderViewScrollView alloc]initWithFrame:frame]; [self addSubview:self.bannerpageControl]; // set KVO [self.bannerScrView addObserver:selfforKeyPath:@"currentPageInteger"options:NSKeyValueObservingOptionNew context:nil]; [self addSubview:self.bannerScrView]; / / set the dots in the top layer to prevent ScrollView cover [self bringSubviewToFront: self. BannerpageControl]; }return self;
}
Copy the code
In the show method invocation ZWCHeaderViewScrollView. H the show method, and it passed the data at the same time set up some pageControl properties.
- (void)showWith:(NSArray *)imageViewUrlArray{
[self.bannerScrView showWith:imageViewUrlArray];
self.bannerpageControl.numberOfPages = imageViewUrlArray.count;
self.bannerpageControl.userInteractionEnabled = NO;
self.bannerpageControl.currentPage = 0;
self.bannerpageControl.currentPageIndicatorTintColor = [UIColor redColor];
self.bannerpageControl.pageIndicatorTintColor = [UIColor yellowColor];
}
Copy the code
The implementation of KVO callback method, the pageControl real-time Settings.
//kvo - (void) observeforkeypath :(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{if ([keyPath isEqualToString:@"currentPageInteger"]) {
self.bannerpageControl.currentPage = [[change valueForKey:@"new"] integerValue];
NSLog(@"Current page changed ------%@",[change valueForKey:@"new"]); }}Copy the code
Finally, remove KVO from the dealloc of ZWCheaderView and stop the timer from the dealloc of ZWCHeaderViewScrollView. Here involves the two possible pit problems I said above, I hope god can give advice.
ZWCheaderView. M
- (void)dealloc {// Remove KVO, check if incomplete [_bannerScrView removeObserver:self] is removedforKeyPath:@"currentPageIntegere"];
}
Copy the code
ZWCHeaderViewScrollView. M
-(void)dealloc{// Stop the timer to prevent crash [_rotateTimer invalidate]; }Copy the code
Well, that’s it. Thank you for reading all the time. I hope you can correct any mistakes or improvements in the article.