股票场内基金交易,没时间盯盘?
UIScrollView 介绍
是一个可以选择滑动的视图,用于显示更多的内容,有着广泛的运用,同时也支持通过手势放大或者缩小显示更多的内容。
常用属性
-
CGSize contentSize :设置 UIScrollView 的滚动范围,要注意区分一个 UIScrollView 的 contentSize 属性和其 frame.size 属性,前者表示的是这个 UIScrollView 内容的大小,而后者表示的是 UIScrollView 在手机屏幕上显示的区域的大小。此外,contentSize 属性只能用代码设置,而且必须要进行设置,否则无法滚动。
-
CGPoint contentOffset:表示界面初始化时 UIScrollView 当前滚动的位置。
-
UIEdgeInsets contentInset :这个属性可以在四周增加滚动范围,其中
UIEdgeInsets 是:123typedef struct UIEdgeInsets {CGFloat top, left, bottom, right;} UIEdgeInsets;分别表示上、左、下、右四个方向的滚动范围。
其他属性
- id
delegate — 设置协议 - BOOL bounces — 是否有弹簧效果
- BOOL scrollEnabled — 是否能滚动
- BOOL showsHorizontalScrollIndicator — 是否显示水平方向的滚动条
- BOOL showsVerticalScrollIndicator — 是否显示垂直方向的滚动条
-
UIScrollViewIndicatorStyle indicatorStyle — 设定滚动条的样式:
12345typedef enum { //指示器风格设置UIScrollViewIndicatorStyleDefault, //默认,UIScrollViewIndicatorStyleBlack, //适用于白色内容背景UIScrollViewIndicatorStyleWhite} UIScrollViewIndicatorStyle; -
BOOL dragging — 是否正在被拖拽
-
BOOL tracking — 当 touch 后还没有拖动的时候值是 YES ,否则 NO
-
BOOL decelerating — 是否正在减速
-
BOOL zooming — 是否正在缩放
-
BOOL pagingEnabled — 是否分页(将在下文制作图片轮播使用)
-
UIEdgeInsets scrollIndicatorInsets — 指定滚动条在scrollerView中的位置
-
BOOL delaysContentTouches — 控制视图是否延时调用开始滚动的方法
-
BOOL canCancelContentTouches — 控制控件是否接触取消touch的事件
-
float minimumZoomScale — 缩小的最小比例
-
float maximumZoomScale — 放大的最大比例
-
float zoomScale — 设置变化比例
-
BOOL bouncesZoom — 控制缩放的时候是否会反弹
-
BOOL zoomBouncing — 判断是否正在进行缩放反弹
-
BOOL scrollsToTop — 控制控件滚动到顶部
UIScrollView 无法滚动?
如果 UIScrollView 无法滚动,可能是以下原因:
- 没有设置 contentSize
- scrollEnabled = NO
- 没有接收到触摸事件 : userInteractionEnabled = NO
- 没有取消 autoLayout 功能
- UIEdgeInsets contentInset 设置太小
手势缩放功能
我们假设在这个 UISCrollView 里面有个 UIImageView ,需要对厘米图片进行缩放:
-
设置代理,实现缩放遵循 UIScrollView 代理协议,这样才能调用 viewForZoomingInScrollView: 方法,将 @interface 改为
1@interface ViewController ()<UIScrollViewDelegate>并在后面代码实现添加代理的对象:
1self.scrollView.delegate = self; -
设置缩放的对象,例如
123- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{return self.imageView;}这个方法在监测到发生缩放行为时会被调用。
-
设置缩放的范围,例如
12self.scrollView.maximumZoomScale = 2.0;self.scrollView.minimumZoomScale = 0.5;
如果程序运行与 Simulator 模拟器,那么按下 option 键加上三指拖动就可以看到缩放效果了。
UIScrollViewDelegate 协议中的各个方法
在 < UIScrollViewDelegate > 协议中有许多方法对应着不同的触发时机触发时机,如上文中手势缩放一样,使用时需要先声明遵循 < UIScrollViewDelegate > 协议并且设置代理。常见的方法包括:
响应滚动和拖拽的:
-
滚动事件(会一直触发): – (void)scrollViewDidScroll:(UIScrollView *)scrollView
-
开始拖拽事件:- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
-
将要结束拖拽事件(带有力度判断,以及偏移量): – (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
velocity 表示对比判断的力度,targetContentOffset 表示达到判断条件的移动位置。
-
完成结束结束拖拽事件: – (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
decelerate 为 YES 时,表示手离开屏幕继续拖拽会保持惯性,为 NO 时则会立即停止。
-
是否可以滚动到顶部: – (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
-
当滚动到顶部时: – (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
-
手指离开屏幕开始保持惯性滑动时: – (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
-
手指离开屏幕保持惯性滑动结束时:- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
响应缩放的:
-
当开始缩放时,返回需要指明缩放的对象:- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
-
当开始缩放时,触发: – (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
-
当结束缩放时,触发: – (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
-
缩放时(一直会)触发:- (void)scrollViewDidZoom:(UIScrollView *)scrollView
响应滚动动画的:
-
方法名为:- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
它用于 setContentOffset:animated: 和 scrollRectToVisible:animated: (代码实现的动画滚动)的后面。
利用 UIScrollView 实现图片轮播
本例的工程文件见wakemeup-xyz@GitHub小站
MVC 思想
使用 OC 中标准的 MVC 思想来构造,它包括:
-
UI
- UIscrollView
- 图片(代码实现)
- UIPageControl (包括三个重要属性 pages、Tint Color、Current page)
-
业务
- 定时自动滚动
- 拖动翻页
- 页码设置(通过代理:1. 协议 2. 设置代理)
UI 部分
其中图片加载到 UIscrollView 是通过代码实现的(因为图片数目内容不固定)例如这里使用一个循环建立 5 个 UIImageView 来载入图片,这 5 个UIImageView 是 UIscrollView 的 subviews ,并且横向排列。 每个 UIImageView 的大小即等于 UIscrollView 的大小:
1 2 3 4 5 6 7 8 9 10 |
for (int i = 0;i < 5;i++) { UIImageView *imageView = [[UIImageView alloc] init]; imageView.frame = CGRectMake(i * self.scrollView.frame.size.width , 0,self.scrollView.frame.size.width, self.scrollView.frame.size.height); imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"img_0%d",i+1]]; [self.scrollView addSubview:imageView]; } |
对 scrollView 的初始化设置:
1 2 3 |
// scrollView 的 contentSize (为五个图片框之和) self.scrollView.contentSize = CGSizeMake(5 * self.scrollView.frame.size.width, self.scrollView.frame.size.height); |
另外添加一个 UIPageControl 到合适的地方,它是 iOS 自带的表示页码的控件,其 Pages 属性可以设置一共有几个图,而 current page 表示当前是第几个图(从 0 开始),可以通过 scrollView 的 offSet 值来判断。
业务实现部分
拖动分页
1 |
self.scrollView.pagingEnabled = YES; |
在设置里拖动分页之后,每次移动的距离即为一个 self.scrollView.frame.size.width 的距离,即每次拖动就会拖动一张图片。
页码设置
页码设置需要先设置代理:
1 |
@interface ViewController ()<UIScrollViewDelegate> |
和
1 |
self.scrollView.delegate = self; |
然后实现 scrollViewDidScroll: 方法(只要有滚动,即 contentOffset 变化即会触发)
1 2 3 4 5 6 7 8 9 |
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { //滚动位置偏移量 CGFloat offsetX = self.scrollView.contentOffset.x; int pageNumber = (offsetX + 0.5* self.scrollView.frame.size.width) / self.scrollView.frame.size.width; NSLog(@"%d",pageNumber); self.pageControl.currentPage = pageNumber; } |
效果如图,当图片划过一半时页码就会变化:
定时滚动
通过定时器 NSTimer 来实现:
1 |
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(nextPage) userInfo:nil repeats:YES]; |
然后实现 nextPage 方法。
我看的范例实现的方法是这样的;
1 2 3 4 5 6 7 8 9 10 |
int currentPage = self.pageControl.currentPage; currentPage ++; if (currentPage == 5) { currentPage = 0; } CGFloat width = self.scrollView.frame.size.width; CGPoint offset = CGPointMake(currentPage * width, 0.f); self.scrollView.contentOffset = offset; |
但是这个的代码在运行时,如果手托住图片几秒不动,按照代码,其 self.scrollView.contentOffset 还是在每秒不断的变化,因此当松手时图片可能会变化几页,解决方法是需要在 scrollViewWillBeginDragging 中暂时关闭定时器,在 scrollViewDidEndDragging 再开启定时器 比较麻烦。
因此我改为了:
1 2 3 4 5 6 7 8 9 |
if (self.pageControl.currentPage == 4) { self.pageControl.currentPage = 0; self.scrollView.contentOffset = CGPointMake(0.f, 0.f); } else { CGPoint currentOffset = self.scrollView.contentOffset; self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width + currentOffset.x, 0.f); } |
这样依照当然的 currentPage 修改,就不会出现拖住不动时会后台翻页的 Bug 了。
进而将代码优化为:
1 |
self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width * ((++self.pageControl.currentPage) % 5), 0.f); |
这是就实现了无动画的定时滚动,如图
[]
加入动画
将 nextPage 的代码改为:
1 |
[self.scrollView setContentOffset:CGPointMake(self.scrollView.frame.size.width * ((++self.pageControl.currentPage) % 5), 0.f) animated:YES]; |
或者是:
1 |
[UIView animateWithDuration:0.3f animations:^{self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width * ((++self.pageControl.currentPage) % 5), 0.f);}]; |
优化
这个时候,图片轮播以及基本满足要求了,但是如果这个 app 还有别的 scrollView,例如,添加一个 Text View。运行程序时,如果拖动这个 Text View 。那么图片轮播就会卡住,如图:
这是因为,语句
1 |
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(nextPage) userInfo:nil repeats:YES]; |
以为着把 nextPage 添加到 main runloop 中来实现循环运行。而 runloop 有两种模式: NSDefaultRunLoopMode 和 NSRunLoopCommonModes,这种语法使用前者,只能同时运行一个事件。要同时运行多个事件需要用 NSRunLoopCommonModes。使用的方法是:
1 2 3 |
NSTimer *timer = [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(nextPage) userInfo:nil repeats:YES]; //需要手动添加到 runloop [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; |
这样不同的 scrollView 也不会影响了。
想获得去掉 5 元限制的证券账户吗?

如果您想去掉最低交易佣金 5 元限制,使用微信扫描左边小程序二维码,访问微信小程序「优财助手」,点击底部菜单「福利」,阅读文章「通过优财开证券账户无最低交易佣金 5 元限制」,按照文章步骤操作即可获得免 5 元证券账户,股票基金交易手续费率万 2.5。
请注意,一定要按照文章描述严格操作,如错误开户是无法获得免 5 元证券账户的。