baozoudiudiu / CWCarousel

轮播图banner

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

oc版本为啥不用nstimer,而用performSelector

syp1027825441 opened this issue · comments

performSelector会在页面被销毁的时候继续执行导致crash

//
// CWCarousel.m
// CWCarousel
//
// Created by WangChen on 2018/4/3.
// Copyright © 2018年 ChenWang. All rights reserved.
//

#import "CWCarousel.h"

@interface CWTempleteCell: UICollectionViewCell
@EnD

@implementation CWTempleteCell
@EnD

@interface CWCarousel ()<UICollectionViewDelegate, UICollectionViewDataSource> {

}
@Property (nonatomic, strong) UICollectionView carouselView;
@Property (nonatomic, assign) NSInteger numbers;
@Property (nonatomic, assign) NSInteger currentIndex;
@Property (nonatomic, assign) NSInteger infactIndex;
@Property (nonatomic, assign) CGFloat addHeight;
/
*
自动播放是否暂停
*/
@Property (nonatomic, assign) BOOL isPause;

/**
当前展示在中间的cell下标
*/
@Property (nonatomic, strong) NSIndexPath *currentIndexPath;

@EnD
@implementation CWCarousel
@synthesize carouselView = _carouselView;

  • (instancetype)initWithFrame:(CGRect)frame delegate:(id)delegate datasource:(id)datasource flowLayout:(CWFlowLayout )flowLayout {
    CGFloat addHeight = 0;
    // if(flowLayout.style == CWCarouselStyle_H_3) {
    // /
    如果是CWCarouselStyle_H_3, 因为中间一张图片放大的原因,需要扩充一下frame的高度,所以会和实际的传入的frame
    // 的高度有部分偏差
    // */
    // addHeight = (flowLayout.maxScale - 1) * CGRectGetHeight(frame);
    // }
    frame.size.height += addHeight;
    self.addHeight = addHeight;
    if(self = [super initWithFrame:frame]) {
    _flowLayout = flowLayout;
    self.delegate = delegate;
    self.datasource = datasource;
    self.isAuto = NO;
    self.autoTimInterval = 3;
    self.endless = YES;
    [self configureView];
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appBecomeInactive:) name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    return self;
    }

  • (void)appBecomeInactive:(NSNotification *)notification {
    [self adjustErrorCell:YES];
    }

  • (void)appBecomeActive:(NSNotification *)notification {
    [self adjustErrorCell:YES];
    }

  • (void)controllerWillAppear {
    if(self.isAuto) {
    [self resumePlay];
    }
    [self adjustErrorCell:YES];
    }

  • (void)controllerWillDisAppear {
    if(self.isAuto) {
    [self pause];
    }
    [self adjustErrorCell:YES];
    }

  • (void)dealloc {

    [self destoryTimer];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    }

  • (void)willMoveToSuperview:(UIView *)newSuperview {
    newSuperview.clipsToBounds = NO;
    if(self.customPageControl == nil && self.pageControl.superview == nil) {
    [self addSubview:self.pageControl];
    self.pageControl.translatesAutoresizingMaskIntoConstraints = NO;
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[control]-0-|"
    options:kNilOptions
    metrics:nil
    views:@{@"control" : self.pageControl}]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[control(30)]-0-|"
    options:kNilOptions
    metrics:nil
    views:@{@"control" : self.pageControl}]];
    }else if(self.customPageControl || self.customPageControl.superview == nil) {
    [self addSubview:self.customPageControl];
    }
    [super willMoveToSuperview:newSuperview];
    }

  • (void)registerViewClass:(Class)viewClass identifier:(NSString *)identifier {
    [self.carouselView registerClass:viewClass forCellWithReuseIdentifier:identifier];
    }

  • (void)registerNibView:(NSString *)nibName identifier:(NSString *)identifier {
    [self.carouselView registerNib:[UINib nibWithNibName:nibName bundle:[NSBundle mainBundle]]
    forCellWithReuseIdentifier:identifier];
    }

  • (void)freshCarousel {
    if([self numbers] <= 0) {
    return;
    }
    [self.carouselView reloadData];
    [self.carouselView layoutIfNeeded];
    if (self.endless)
    [self.carouselView scrollToItemAtIndexPath:[self originIndexPath] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    else
    {
    if(self.flowLayout.style == CWCarouselStyle_Normal)
    {
    [self.carouselView scrollToItemAtIndexPath:self.currentIndexPath = [NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    }
    else
    {
    [self.carouselView scrollToItemAtIndexPath:self.currentIndexPath = [NSIndexPath indexPathForRow:1 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    }
    }

    self.carouselView.userInteractionEnabled = YES;
    if (self.isAuto) {
    [self play];
    }
    }
    #pragma mark - < Scroll Delegate >
    /// 开始拖拽

  • (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    // 防止拖动加速度太大,一次跳过多张图片,这里设置一下
    if (@available(iOS 14.0, *)) {
    scrollView.pagingEnabled = NO;
    } else {
    scrollView.pagingEnabled = YES;
    }
    if (self.isAuto) {
    [self stop];
    }
    if (self.delegate && [self.delegate respondsToSelector:@selector(CWCarousel:didStartScrollAtIndex:indexPathRow:)]) {
    [self.delegate CWCarousel:self didStartScrollAtIndex:[self caculateIndex:self.currentIndexPath.row] indexPathRow:self.currentIndexPath.row];
    }
    }

/// 将要结束拖拽

  • (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (!self.endless)
    {
    NSInteger maxIndex = [self numbers] - 1;
    NSInteger minIndex = 0;
    if(self.flowLayout.style != CWCarouselStyle_Normal)
    {
    maxIndex = [self infactNumbers] - 2;
    minIndex = 1;
    }
    if (velocity.x == 0) {
    //还有一种情况,当滑动后手指按住不放,然后松开,此时的加速度其实是为0的
    [self adjustErrorCell:NO];
    if (@available(iOS 14.0, *)) {
    // iOS14以前,就算加速度为0,后续系统会还是会走scrollViewWillBeginDecelerating:回调
    // 但是iOS14以后,加速度为0时,不会在后续执行回调.这里手动触发一下
    [self scrollViewWillBeginDecelerating:self.carouselView];
    }
    return;
    }
    if (velocity.x >= 0 && self.currentIndexPath.row == maxIndex)
    {
    return;
    }
    if (velocity.x <= 0 && self.currentIndexPath.row == minIndex)
    {
    return;
    }
    }

    if(velocity.x > 0) {
    //左滑,下一张
    self.currentIndexPath = [NSIndexPath indexPathForRow:self.currentIndexPath.row + 1 inSection:self.currentIndexPath.section];
    }else if (velocity.x < 0) {
    //右滑,上一张
    self.currentIndexPath = [NSIndexPath indexPathForRow:self.currentIndexPath.row - 1 inSection:self.currentIndexPath.section];
    }else if (velocity.x == 0) {
    //还有一种情况,当滑动后手指按住不放,然后松开,此时的加速度其实是为0的
    [self adjustErrorCell:NO];
    if (@available(iOS 14.0, *)) {
    // iOS14以前,就算加速度为0,后续系统会还是会走scrollViewWillBeginDecelerating:回调
    // 但是iOS14以后,加速度为0时,不会在后续执行回调.这里手动触发一下
    [self scrollViewWillBeginDecelerating:self.carouselView];
    }
    }
    }

/// 开始减速

  • (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {

    if(self.currentIndexPath != nil &&
    self.currentIndexPath.row < [self infactNumbers] &&
    self.currentIndexPath.row >= 0) {
    // 中间一张轮播,居中显示
    if (!self.endless)
    {
    if (self.currentIndexPath.row == 0 && self.style != CWCarouselStyle_Normal)
    {
    self.currentIndexPath = [NSIndexPath indexPathForRow:1 inSection:self.currentIndexPath.section];
    }
    else if (self.currentIndexPath.row == [self infactNumbers] - 1 && self.style != CWCarouselStyle_Normal)
    {
    self.currentIndexPath = [NSIndexPath indexPathForRow:[self infactNumbers] - 2 inSection:self.currentIndexPath.section];
    }
    }
    [self.carouselView scrollToItemAtIndexPath:self.currentIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
    }
    }

/// 减速完成

  • (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

    // 打开交互
    scrollView.pagingEnabled = NO;
    if(self.isAuto) {
    [self play];
    }
    }

/// 滚动动画完成

  • (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {

    // 滚动完成,打开交互,关掉pagingEnabled
    // 为什么要关掉pagingEnabled呢,因为切换控制器的时候会有系统级bug,不信你试试.
    scrollView.userInteractionEnabled = YES;
    scrollView.pagingEnabled = NO;
    if(self.isAuto) {
    [self play];
    }
    if (self.endless)
    [self checkOutofBounds];

// if(!self.endless)
// {
// CGFloat space = self.flowLayout.itemSpace_H + self.flowLayout.itemWidth * (1 - self.flowLayout.minScale) * 0.5;
// if(self.currentIndexPath.row == 0)
// self.carouselView.contentInset = UIEdgeInsetsMake(0, space, 0, 0);
// else if(self.currentIndexPath.row == [self numbers] - 1)
// self.carouselView.contentInset = UIEdgeInsetsMake(0, 0, 0, space);
// else
// self.carouselView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
// }

if (self.delegate && [self.delegate respondsToSelector:@selector(CWCarousel:didEndScrollAtIndex:indexPathRow:)]) {
    [self.delegate CWCarousel:self didEndScrollAtIndex:[self caculateIndex:self.currentIndexPath.row] indexPathRow:self.currentIndexPath.row];
}

}

// 滚动中

  • (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // 滚动过程中关闭交互
    // scrollView.userInteractionEnabled = NO;
    }
    #pragma mark - < Logic Helper >

  • (NSIndexPath *)originIndexPath {

    NSInteger num = [self numbers];
    if (num <= 0) {
    return [[NSIndexPath alloc] initWithIndex:0];
    }

    NSInteger centerIndex = [self infactNumbers] / [self numbers];

    if (self.endless) {
    if(centerIndex <= 1) {
    if (centerIndex == 1) {
    self.currentIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    }else {
    self.currentIndexPath = [NSIndexPath indexPathForRow:self.numbers inSection:0];
    }
    }else {
    self.currentIndexPath = [NSIndexPath indexPathForRow:centerIndex / 2 * [self numbers] inSection:0];
    }
    }else {
    NSInteger row = self.flowLayout.style == CWCarouselStyle_Normal ? 0 : 1;
    self.currentIndexPath = [NSIndexPath indexPathForRow:row inSection:0];
    }

    return self.currentIndexPath;
    }

  • (void)checkOutofBounds {
    if ([self numbers] <= 0) {return;}
    // 越界检查
    if(self.currentIndexPath.row == [self infactNumbers] - 1) {
    //最后一张
    NSIndexPath *origin = [self originIndexPath];
    NSInteger index = [self caculateIndex:self.currentIndexPath.row] - 1;
    self.currentIndexPath = [NSIndexPath indexPathForRow:origin.row + index inSection:origin.section];
    [self.carouselView scrollToItemAtIndexPath:self.currentIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    self.carouselView.userInteractionEnabled = YES;
    }else if(self.currentIndexPath.row == 0) {
    //第一张
    NSIndexPath *origin = [self originIndexPath];
    NSInteger index = [self caculateIndex:self.currentIndexPath.row];
    self.currentIndexPath = [NSIndexPath indexPathForRow:origin.row + index inSection:origin.section];
    [self.carouselView scrollToItemAtIndexPath:self.currentIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    self.carouselView.userInteractionEnabled = YES;
    }
    }

  • (void)pageControlClick:(UIPageControl *)sender {
    if (![sender isKindOfClass:[UIPageControl class]]) {
    return;
    }
    NSInteger page = sender.currentPage;
    NSInteger prePage = [self caculateIndex:self.currentIndexPath.row];
    if(page == prePage) {
    return;
    }
    NSIndexPath *indexPath = nil;
    if(prePage - page == [self numbers] - 1) {
    //最后一张跳到第一张
    indexPath = [NSIndexPath indexPathForRow:self.currentIndexPath.row + 1 inSection:0];
    }else if(page - prePage == [self numbers] - 1) {
    //第一张跳到最后一张
    indexPath = [NSIndexPath indexPathForRow:self.currentIndexPath.row - 1 inSection:0];
    }else {
    indexPath = [NSIndexPath indexPathForRow:self.currentIndexPath.row + page - prePage inSection:0];
    }
    [self.carouselView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
    self.currentIndexPath = indexPath;
    }

/**
实际下标转换成业务需求下标

@param factIndex 实际下标
@return 业务需求下标
*/

  • (NSInteger)caculateIndex:(NSInteger)factIndex {
    if (self.numbers <= 0) {
    return 0;
    }
    NSInteger row = factIndex % [self numbers];
    if(!self.endless && self.flowLayout.style != CWCarouselStyle_Normal)
    {
    row = factIndex % [self infactNumbers] - 1;
    }
    return row;
    }

  • (void)adjustErrorCell:(BOOL)isScroll {
    NSArray <NSIndexPath *> *indexPaths = [self.carouselView indexPathsForVisibleItems];
    NSMutableArray <UICollectionViewLayoutAttributes *> *attriArr = [NSMutableArray arrayWithCapacity:indexPaths.count];
    [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    UICollectionViewLayoutAttributes *attri = [self.carouselView layoutAttributesForItemAtIndexPath:obj];
    [attriArr addObject:attri];
    }];
    CGFloat centerX = self.carouselView.contentOffset.x + CGRectGetWidth(self.carouselView.frame) * 0.5;
    __block CGFloat minSpace = MAXFLOAT;
    // BOOL shouldSet = YES;
    // if (self.flowLayout.style != CWCarouselStyle_Normal && indexPaths.count <= 2)
    // {
    // shouldSet = NO;
    // }
    [attriArr enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    obj.zIndex = 0;
    // if(ABS(minSpace) > ABS(obj.center.x - centerX) && shouldSet) {
    // minSpace = obj.center.x - centerX;
    // self.currentIndexPath = obj.indexPath;
    // }
    if(ABS(minSpace) > ABS(obj.center.x - centerX)) {
    minSpace = obj.center.x - centerX;
    self.currentIndexPath = obj.indexPath;
    }
    }];
    if(isScroll) {
    [self scrollViewWillBeginDecelerating:self.carouselView];
    }
    }

  • (void)play {
    [self stop];
    if(self.isPause) {
    return;
    }
    [self showTimer];
    // [self performSelector:@selector(nextCell) withObject:nil afterDelay:self.autoTimInterval];
    }

  • (void)showTimer
    {
    if (!_timer) {
    //开始倒计时
    WS(weakSelf)
    _timer = [NSTimer bk_scheduledTimerWithTimeInterval:self.autoTimInterval block:^(NSTimer *timer) {
    [weakSelf nextCell];
    } repeats:YES];
    // [_timer setFireDate:[NSDate date]];
    //不受滚动事件影响
    [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
    } else {
    [_timer setFireDate:[NSDate dateWithTimeInterval:self.autoTimInterval sinceDate:[NSDate date]]];
    }
    }

-(void)destoryTimer
{
if (_timer) {
[_timer invalidate];
_timer = nil;
}
}

  • (void)nextCell {
    if([self numbers] <= 0) {
    return;
    }
    NSInteger maxIndex = 1;
    if(!self.endless && self.flowLayout.style != CWCarouselStyle_Normal)
    {
    maxIndex = 2;
    }
    if(self.currentIndexPath.row < [self infactNumbers] - maxIndex)
    {
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.currentIndexPath.row + 1 inSection:self.currentIndexPath.section];
    [self.carouselView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
    self.currentIndexPath = indexPath;
    }
    else if(!self.endless)
    {
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:maxIndex - 1 inSection:self.currentIndexPath.section];
    [self.carouselView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
    self.currentIndexPath = indexPath;
    }
    // [self performSelector:@selector(nextCell) withObject:nil afterDelay:self.autoTimInterval];
    }

  • (void)stop {
    if (_timer) {
    [_timer setFireDate:[NSDate distantFuture]];
    }
    // [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(nextCell) object:nil];
    // [NSObject cancelPreviousPerformRequestsWithTarget:self];
    }

  • (void)resumePlay {
    self.isPause = NO;
    [self play];
    }

  • (void)pause {
    self.isPause = YES;
    [self stop];
    }

  • (void)releaseTimer {
    [self stop];
    }

#pragma mark - < Configure View>

  • (void)configureView {
    self.backgroundColor = [UIColor blackColor];
    self.carouselView.showsVerticalScrollIndicator = NO;
    self.carouselView.showsHorizontalScrollIndicator = NO;
    self.carouselView.decelerationRate = 0;
    }

#pragma mark - < Delegate, Datasource >

  • (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
    if(!self.endless
    && self.flowLayout.style != CWCarouselStyle_Normal
    && (indexPath.row == 0 || indexPath.row == [self infactNumbers] - 1))
    {
    // 添加占位cell
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"tempCell" forIndexPath:indexPath];
    cell.contentView.backgroundColor = [UIColor clearColor];
    return cell;
    }
    else
    {
    if(self.datasource &&
    [self.datasource respondsToSelector:@selector(viewForCarousel:indexPath:index:)])
    {
    UICollectionViewCell *cell = [self.datasource viewForCarousel:self indexPath:indexPath index:[self caculateIndex:indexPath.row]];
    return cell;
    }
    return nil;
    }
    }

  • (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    if([self numbers] > 0)
    return [self infactNumbers];
    else
    return 0;
    }

  • (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    if(self.delegate &&
    [self.delegate respondsToSelector:@selector(CWCarousel:didSelectedAtIndex:)]) {
    [self.delegate CWCarousel:self didSelectedAtIndex:[self caculateIndex:indexPath.row]];
    }
    // 处于动画中时,点击cell,可能会出现cell不居中问题.这里处理下
    // 将里中心点最近的那个cell居中
    [self adjustErrorCell:YES];
    }

#pragma mark -

  • (void)setBackgroundColor:(UIColor *)backgroundColor {
    self.carouselView.backgroundColor = backgroundColor;
    [super setBackgroundColor:backgroundColor];
    }

  • (void)setCurrentIndexPath:(NSIndexPath *)currentIndexPath {
    _currentIndexPath = currentIndexPath;
    if(self.customPageControl == nil)
    self.pageControl.currentPage = [self caculateIndex:currentIndexPath.row];
    else
    self.customPageControl.currentPage = [self caculateIndex:currentIndexPath.row];
    }

  • (void)setEndless:(BOOL)endless {
    if(_endless != endless)
    {
    _endless = endless;
    }
    }

#pragma mark - < getter >

  • (UICollectionView *)carouselView {
    if(!_carouselView) {
    // self.carouselView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, self.addHeight * 0.5, self.frame.size.width, self.frame.size.height - self.addHeight) collectionViewLayout:self.flowLayout];
    self.carouselView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.flowLayout];
    _carouselView.clipsToBounds = NO;
    _carouselView.delegate = self;
    _carouselView.dataSource = self;
    _carouselView.translatesAutoresizingMaskIntoConstraints = NO;
    [_carouselView registerClass:[CWTempleteCell class] forCellWithReuseIdentifier:@"tempCell"];
    [self addSubview:_carouselView];

      NSDictionary *views = @{@"view" : self.carouselView};
      NSDictionary *margins = @{@"top" : @(self.addHeight * 0.5),
                                @"bottom" : @(self.addHeight * 0.5)
                                };
      NSString *str = @"H:|-0-[view]-0-|";
      [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:str
                                                                   options:kNilOptions
                                                                   metrics:margins
                                                                     views:views]];
      str = @"V:|-top-[view]-top-|";
      [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:str
                                                                   options:kNilOptions
                                                                   metrics:margins
                                                                     views:views]];
    

    }
    return _carouselView;
    }

  • (CWCarouselStyle)style {
    if(self.flowLayout)
    {
    return self.flowLayout.style;
    }
    return CWCarouselStyle_Unknow;
    }

/**
Description

@return 业务需求需要展示轮播图个数
*/

  • (NSInteger)numbers {
    if(self.datasource &&
    [self.datasource respondsToSelector:@selector(numbersForCarousel)]) {
    self.pageControl.numberOfPages = [self.datasource numbersForCarousel];
    return self.pageControl.numberOfPages;
    }
    return 0;
    }

/**
轮播图实际加载视图个数

@return 轮播图实际加载视图个数
*/

  • (NSInteger)infactNumbers {

    NSInteger num = [self numbers];

    if ( 0 >= num) {
    return 0;
    }

    [self.carouselView setScrollEnabled:YES];

    if (self.endless)
    {
    if (num < 1) {
    [self.carouselView setScrollEnabled:NO];
    return num;
    }
    // 如果是无限轮播,默认加载300个
    return 300;
    }
    else
    {
    // 如果不是无限轮播,出了第一种样式,其他的样式要加2个占位空cell
    if(self.flowLayout.style == CWCarouselStyle_Normal)
    {
    return [self numbers];
    }
    else
    {
    if (num == 1) {
    [self.carouselView setScrollEnabled:NO];
    }
    // 前后2个占位cell,所以+2
    return [self numbers] + 2;
    }
    }
    }

  • (UIPageControl *)pageControl {
    if(!_pageControl) {
    self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 0, 300, 30)];
    CGPoint center = self.center;
    center.y = CGRectGetHeight(self.frame) - 30 * 0.5;
    _pageControl.pageIndicatorTintColor = [UIColor blackColor];
    _pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];
    _pageControl.userInteractionEnabled = NO;
    [_pageControl addTarget:self action:@selector(pageControlClick:) forControlEvents:UIControlEventTouchUpInside];
    _pageControl.center = center;
    }
    return _pageControl;
    }

  • (NSString *)version {
    return @"1.1.7";
    }

#pragma mark - Setter

  • (void)setCustomPageControl:(UIView *)customPageControl {
    _customPageControl = customPageControl;
    if(_customPageControl && _customPageControl.superview == nil)
    {
    [self addSubview:_customPageControl];
    [self bringSubviewToFront:_customPageControl];
    if(self.pageControl.superview == _customPageControl.superview)
    {
    [self.pageControl removeFromSuperview];
    }
    }
    }
    @EnD

@Property (nonatomic, weak) NSTimer *timer;这段代码加到.h文件
NSTimer用的是#import "NSTimer+BlocksKit.h",和控制器解耦,避免内存泄漏

不管是使用timer还是perform,都是要在对应的地方编写移除代码的。个人认为其实是没有太大差别的(当然timer功能上会更强大一些)。控件有对应的销毁方法暴露在.h文件中,你应该是没有调用而导致的。