摆脱第三方库系列(二)- 自己写一个滚动广告

winniebnu 8年前
   <h2>前言</h2>    <p>摆脱第三方库系列第二章,我将介绍滚动广告的写法。在一些信息聚合类APP中滚动广告非常常见或者说必不可少,他可以不需要用户操作展示一些开发者想展示的东西,原理其实也很简单。我写的滚动广告主要实现自动滚动和循环滚动这两个功能,至于广告的点击事件或者美化这都是后话了。</p>    <h2>滚动广告基本原理</h2>    <p>其实只要是滚动的东西都离不开UISCrollView,滚动广告的原理就是在一个横向的UISCrollView上放置多张图片并插入文字,然后设置时间自动翻页,点击事件可以通过设置代理方法完成。</p>    <p>而循环播放的原理是这样的,假使我们有五张广告图需要播放,那么我们在UISCrollView应该插入七张图片,除开头尾两张后中间剩下的五张按顺序放置我们要展示的广告,那么头尾两部分放什么呢?</p>    <blockquote>     <p>头部分应该放最后一张也就是第五张广告图,尾部分应该放第一张广告图</p>    </blockquote>    <p>这样当用户看到第一张广告图并向左移动时就会出现第五张广告图,然后我们应该在代码中使用一些“小手段”,当广告页滚动到头页的时候应该自动跳到倒数第二页也就是第五张广告页。这样就完成了一个看起来可以循环的轮播广告了。</p>    <p>代码实现</p>    <p>先上完成效果图</p>    <p><img alt="摆脱第三方库系列(二)- 自己写一个滚动广告" src="https://simg.open-open.com/show/23e423f91393e9b69a00901a8d454427.gif" width="366" height="645"></p>    <p>接下来上代码,先创建一个继承于UIView的子类RollView,这里不推荐直接继承于UISCrollView,那样可能会因为控件层次问题出现有些控件被覆盖了。</p>    <h2>接口部分</h2>    <p>RollView接口部分如下</p>    <pre>  <code class="language-objectivec">@interface RollView : UIView<UIScrollViewDelegate>  @property (nonatomic, strong) UIScrollView *rollScrollView;  @property (nonatomic, strong) NSMutableArray *imageArray;  @property (nonatomic, strong) UIImageView *rollImgView;  @property (nonatomic, strong) UIPageControl *pageControl;  @property (nonatomic, strong) NSTimer *timer;  @end  </code></pre>    <p>UIPageControl是一个苹果公司给的页面小点的控件,也是滚动广告比较常见的需要添加的控件。而<code>imageArray</code>主要是为了存储多个广告图,可以自己封装一个UIImageView这样就可以让自己的广告更加个性了。</p>    <p>为了简化代码先宏定义下</p>    <pre>  <code class="language-objectivec">#define COUNT self.imageArray.count  #define SVIEWWIDTH self.frame.size.width  #define SVIEWHEIGHT self.frame.size.height  </code></pre>    <h2>RollView初始化</h2>    <p>先看自身的初始化方法,很简单,<code>addNSTimer</code>方法是添加一个定时器滚动,一会再介绍。</p>    <pre>  <code class="language-objectivec">- (instancetype)initWithFrame:(CGRect)frame  {      self = [super initWithFrame:frame];      if(self){          [self addSubview:self.rollScrollView];          [self addSubview:self.pageControl];          [self addNSTimer];      }      return self;  }  </code></pre>    <h2>各个控件get方法</h2>    <p>接下来看get方法</p>    <pre>  <code class="language-objectivec">- (UIScrollView *)rollScrollView  {      if (_rollScrollView == nil) {          _rollScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, SVIEWWIDTH, SVIEWHEIGHT)];          _rollScrollView.delegate = self;          _rollScrollView.contentSize = CGSizeMake((COUNT + 2) * SVIEWWIDTH, SVIEWHEIGHT);          //使子视图imageview自动适配scrollview的大小          [_rollScrollView setAutoresizesSubviews:YES];          //将scrollview设成分页形式          [_rollScrollView setPagingEnabled:YES];          //隐藏scrollview两边的滚动条          [_rollScrollView setShowsVerticalScrollIndicator:NO];          [_rollScrollView setShowsHorizontalScrollIndicator:NO];          //设置scrollview初始的位置为第二部分来显示第一张广告          _rollScrollView.contentOffset = CGPointMake(SVIEWWIDTH, 0);      }      return _rollScrollView;  }  </code></pre>    <p>什么是UISCrollView的contentSize我在<a href="http://www.open-open.com/lib/view/open1453422292214.html">这篇文章</a>讲过。由于要循环播放所以要加两张图片,所以宽度为<code>(COUNT + 2) * SVIEWWIDTH</code>,其他解释写在注释里了。</p>    <p>然后是pageControl的get方法。</p>    <pre>  <code class="language-objectivec">- (UIPageControl *)pageControl  {      if (_pageControl == nil) {          _pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake((SVIEWWIDTH - _pageControl.bounds.size.width)/2, SVIEWHEIGHT - 10, _pageControl.bounds.size.width, _pageControl.bounds.size.height)];          _pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];          _pageControl.pageIndicatorTintColor = [UIColor grayColor];          [_pageControl setNumberOfPages:COUNT];      }      return _pageControl;  }  </code></pre>    <p>其中<code>currentPageIndicatorTintColor</code>是点移动到当前点时的颜色,<code>pageIndicatorTintColor</code>是没移动到的点的颜色。</p>    <p>然后在<code>imageArray</code>中添加我们的广告图片,由于NSArray是可以存储任何对象类型的,所以我们可以存储广告图也可以直接存UIImageView,这里我是存了任意的五张UIImage,就不上代码了,直接看<code>rollImgView</code>。</p>    <pre>  <code class="language-objectivec">- (UIImageView *)rollImgView  {      if (_rollImgView == nil) {          //第一个放最后一个图片          _rollImgView = [[UIImageView alloc] init];          _rollImgView.frame = CGRectMake(0, 0, SVIEWWIDTH, SVIEWHEIGHT);          [_rollImgView setImage:[_imageArray objectAtIndex:(COUNT - 1)]];          [self.rollScrollView addSubview:_rollImgView];          //最后一个放第一张图片          _rollImgView = [[UIImageView alloc] init];          _rollImgView.frame = CGRectMake(SVIEWWIDTH * (COUNT + 1), 0, SVIEWWIDTH, SVIEWHEIGHT);          [_rollImgView setImage:[_imageArray objectAtIndex:0]];          [self.rollScrollView addSubview:_rollImgView];                for (int i = 0; i < COUNT; i++) {              _rollImgView = [[UIImageView alloc] init];              _rollImgView.frame = CGRectMake(SVIEWWIDTH * (i + 1), 0, SVIEWWIDTH, SVIEWHEIGHT);              [_rollImgView setImage:[_imageArray objectAtIndex:i]];              [self.rollScrollView addSubview:_rollImgView];          }      }      return _rollImgView;  }  </code></pre>    <p>这里需要注意的就是每个图片需要放置的frame,想不明白的自己动手画个草稿就一目了然了。我是设置了五张广告图片所以我们应该用七张UIImageView,所以要初始化七次添加七次,可能有点不合理,有更好的办法希望留言告诉我。</p>    <h2>scrollView代理部分</h2>    <pre>  <code class="language-objectivec">- (void)scrollViewDidScroll:(UIScrollView *)scrollView  {      if (scrollView.contentOffset.x == 0) {          self.rollScrollView.contentOffset = CGPointMake(COUNT * SVIEWWIDTH, 0);          self.pageControl.currentPage = (COUNT - 1);      }else if (scrollView.contentOffset.x == (COUNT + 1) * SVIEWWIDTH){          self.rollScrollView.contentOffset = CGPointMake(SVIEWWIDTH, 0);          self.pageControl.currentPage = 0;      }else{          int pagenum = (round(scrollView.contentOffset.x/self.rollImgView.frame.size.width) - 1);          [self.pageControl setCurrentPage:pagenum];      }  }    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView  {      [self removeNSTimer];  }    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate  {      [self addNSTimer];  }  </code></pre>    <p>主要代码都在<code>scrollViewDidScroll</code>方法中,就是在scrollview滚动时调用的方法。这里主要就是前面提到的实现循环滚动的“小手段”了。</p>    <p>当<code>scrollView.contentOffset.x == 0</code>时,说明的此时显示的是头imageview即第五张广告,那么我们需要跳动到第五张广告也就是第六张imageview来完成循环效果,同时也别忘记改版pageControl的当前点。由于是5个点,那么下标是0-4,所以是标到<code>(COUNT - 1)</code>处。</p>    <p><code>scrollView.contentOffset.x == (COUNT + 1) * SVIEWWIDTH</code>表示的是在尾imageview时的操作。</p>    <p>如果既不在头也不在尾,我们只需要在滚动时改变标签点就可以了。其中<code>round</code>是表示取四舍五入的整数,这样改变标签点更合理。</p>    <p>下面两个方法是说我们鼠标按住scrollview时我们应该取消计时器不再计时,松开手时再开始计时。</p>    <h2>计时器</h2>    <pre>  <code class="language-objectivec">- (void)addNSTimer  {      if (self.timer == nil) {          NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];          [[NSRunLoop mainRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];          self.timer = timer;      }  }    - (void)removeNSTimer  {      [self.timer invalidate];      self.timer =nil;  }    - (void)nextPage  {      if (self.rollScrollView.contentOffset.x == ((COUNT) * self.rollImgView.frame.size.width)) {          [self.rollScrollView scrollRectToVisible:CGRectMake(SVIEWWIDTH, 0, SVIEWWIDTH, SVIEWHEIGHT) animated:YES];      }else{          [self.rollScrollView scrollRectToVisible:CGRectMake(self.rollScrollView.contentOffset.x + self.rollImgView.frame.size.width, 0, SVIEWWIDTH, SVIEWHEIGHT) animated:YES];      }  }  </code></pre>    <p>这里主要是三个自定义方法,顾名思义都比较简单,这里不做详细介绍了。</p>    <p>接下来就将RollView添加到ViewController中就可以展示了。</p>    <p>总结</p>    <p>这样滚动广告差不多就介绍完了,我只是介绍了基本原理,在我们的开发中肯定是不能完全满足需求的,所以还要加一些自定义的更改。比如用自定义的imageview并用代理方法增加点击事件,或者直接不用imageview用buttun。demo我也会放到我的<a href="/misc/goto?guid=4959671990317529318">github</a>上。</p>    <p>还是那句话,如果有错还请指出,万分感谢。</p>    <p>相关文章</p>    <p><a href="http://www.open-open.com/lib/view/open1462063462734.html">摆脱第三方库系列(一)-自己写一个侧拉菜单</a></p>    <p>来源:http://cbsfly.github.io/ios/rollview</p>