一、UIScrollView使用的步骤


1.创建UIScrollView
2.将需要展示的内容添加到UIScrollView中
3.设置UIScrollView的滚动范围 (contentSize)

 @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@end
// 1.添加两个子控件到UIScrollView中
// 一个控件没有设置frame, 默认x/y就是0
UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
[self.scrollView addSubview:btn]; UISwitch *sw = [[UISwitch alloc] init];
CGRect tempFrame = sw.frame;
tempFrame.origin.y = ;
sw.frame = tempFrame;
[self.scrollView addSubview:sw]; // 添加一个按钮
UIButton *customBtn = [[UIButton alloc] init];
customBtn.frame = CGRectMake(, , , );
customBtn.backgroundColor = [UIColor redColor];
[customBtn setTitle:@"我是按钮" forState:UIControlStateNormal];
[customBtn setTitle:@"我是高亮" forState:UIControlStateHighlighted];
[customBtn setTitle:@"我是disabled状态" forState:UIControlStateDisabled];
//[customBtn addTarget:self action:@selector(customBtnClick) forControlEvents:UIControlEventTouchUpInside];
[self.scrollView addSubview:customBtn]; // 注意: 如果想让UIScrollView进行滚动, 必须设置可以滚动的范围
// 设置scrollView的滚动范围为, frame的宽高 + 100
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width + , self.scrollView.frame.size.height + );

二、scrollView的基本属性


scrollView不能滚动的几种情况
  1.没有设置contentSize
  2.scrollEnabled属性 = NO
  3.userInteractionEnabled属性 = NO

self.scrollView.scrollEnabled = NO;
self.scrollView.userInteractionEnabled = NO;

enabled和userInteractionEnabled的区别
enabled: 代表控件不可用
userInteractionEnabled: 代表控件不可以和用户交互, 也就是不能响应用户的操作

如何去掉滚动条

self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.showsVerticalScrollIndicator = NO;

滚动条也是scrollView的子控件的一部分
滚动条可能在子控件的前面, 也可能在子控件的后面
正是因为这个原始, 所以以后在开发中不推荐通过subviews获取子控件的方式来操作子控件

[self.scrollView.subviews lastObject];

设置滚动条的样式

self.scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;

默认情况下UIScrollView有一个回弹效果
只要设置了contentSize就有回弹效果

self.scrollView.bounces = YES;

设置默认是否有回弹效果 (默认就是没有设置contentSize的情况)
垂直方向可以回弹
下拉刷新
哪怕没有设置contentSize也可以有回弹效果

self.scrollView.alwaysBounceVertical = YES;
self.scrollView.alwaysBounceHorizontal = YES;

设置内容偏移位(contentOffset)

// 其实就是设置scrollView滚动到什么地方
// 告诉scrollView x方向要移动多少, y方向要移动多少
// 如果x是正数: 图片往左边移动
// 如果x是负数: 图片往右边移动
// 同理y是正数: 图片往上移动
// 同理y是负数: 图片往下移动
// 计算公式: 永远都是以 控件的左上角 – 内容的左上角

sc.contentOffset = CGPointMake(, );

// 注意点:contentOffset移动的位置是一个临时的位置, 只要轻轻拖拽一下就会回到默认的位置

// 个人理解: 以图片左上角为原点,sc.contentOffset即UIScrollView相对于图片的偏移量

三、如何监听一个控件的变化/状态


1. 首先需要查看该控件的头文件, 看它继承于谁
  1.1如果继承于UIControl, 那么就可以通过addTarget来监听
  1.2如果继承于UIView, 那么必须通过代理来监听

2. 代理协议的规律:
  以控件的类名开头, 后面加上delegate

3. 代理协议中的方法名的规律:
  一般以控件名称去掉类前缀开头

4. 代理协议中的方法参数的规律:
  谁触发事件, 就将谁传递进来

5. 如何监听UIScrollView的变化
  1.成为UIScrollView的代理
  2.遵守UIScrollView的协议
  3.实现UIScrollView协议中的方法

6.代理作用:
  当A对象想监听B对象的变化 , 那么可以让A成为B的代理
  当B对象发生一些变化想通知A对象, 那么可以让A成为B的代理

@property (weak, nonatomic) IBOutlet UIScrollView *sc;
7.为什么代理要用weak
  原因: 为了防止循环引用
  控制器 -强引用-> 控制器的View -强引用-> subViews数组 -强引用-> UIScrollView -弱引用-> 控制器

如果只有一个控制器的情况, 程序一启动就创建的这个控制器是不会被释放的

strong
对象, 强指针, 强引用
weak
对象, 控件/代理
copy
对象, 字符串, 为了防止外界修改内部的属性的值
assign
基本数据类型 int/float/doble/bool ..

 @interface ViewController ()<UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *sc; @end self.sc.delegate = self; #pragma mark - UIScrollViewDelegate
// 只要成为了UIScrollView的代理, 遵守代理协议, 实现协议中的方法
// 当UIScrollView发生一些变化的时候, 系统就会自动调用这些代理方法 // scrollViewDidScroll方法什么时候调用?
// 只要UIScrollView滚动了, 系统就会自动调用
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(@"%s", __func__);
} // 只要用户准备开始拖拽了就会调用
- (void)scrollViewWillBeginDragging:(nonnull UIScrollView *)scrollView
{
NSLog(@"%s", __func__);
} // 用户已经结束拖拽, 代表用户已经松手了
// 系统调用了该方法并不代表着,UIScrollView已经停止滚动了 // 每次调用 停止拖拽方法时 ,系统都会传入一个当前是否有惯性的参数
// 我们可以判断该参数是否为YES, 如果是YES代表当前UIScrollView有惯性, 停止拖拽并不会停止滚动, 需要在停止减速方法中监听什么时候真正的停止
- (void)scrollViewDidEndDragging:(nonnull UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
NSLog(@"%s", __func__);
if (decelerate == NO) {
// NSLog(@"没有惯性, 可以在当前方法监听UIScrollView是否停止滚动");
[self scrollViewDidEndDecelerating:scrollView];
}else{
// NSLog(@"有惯性, 需要在减速结束方法中监听UIScrollView是否停止滚动");
}
} // UIScrollView已经停止减速了
// 只有执行了这个方法才代表UIScrollView已经停止滚动了
- (void)scrollViewDidEndDecelerating:(nonnull UIScrollView *)scrollView
{
NSLog(@"UIScrollView停止滚动了");
}

注意:
如果想在UIScrollView停止滚动之后做一些操作, 有两种情况
1.没有惯性的情况: 只会调用 停止拖拽的方法, 不会调用停止减速的方法
2.有惯性的情况: 既会调用 停止拖拽的方法, 也会调用停止减速的方法
所以: 以后要判断UIScrollView是否停止滚动, 需要同时重写两个方法
  2.1scrollViewDidEndDragging
  2.2scrollViewDidEndDecelerating

四、缩放图片


要想缩放图片分为两步
  1.成为代理, 通过代理方法告诉UIScrollView要缩放哪一个子控件
  2.设最大置子控件和最小的缩放比例

 // 要想缩放, 除了告诉UISrollView要缩放哪一个控件以外, 还要告诉UISrollView最小能缩多小, 最大能放多大
self.sc.maximumZoomScale = 2.0;
self.sc.minimumZoomScale = 0.5; // 因为所有的子控件都是我们添加进去的, 所以要缩放哪一个我们最清楚
// 所以只要让控制器成为UISrollView的代理, 当UISrollView不清楚要缩放哪一个控件的时候
// UISrollView就会调用它的代理方法, 问问代理到底要缩放哪一个
self.sc.delegate = self; // 因为UISrollView中可能有多个子控件
// 那么UISrollView就搞不清楚到底要缩放哪一个子控件
// 想要缩放, 必须明确的告诉UISrollView要缩放哪一个控件
// 在此方法中告诉UISrollView要缩放哪一个控件
- (nullable UIView *)viewForZoomingInScrollView:(nonnull UIScrollView *)scrollView
{
return self.iv;
} // 缩放的过程中调用
// 和scrollViewDidScroll一样, 只要缩放一点点就会调用
- (void)scrollViewDidZoom:(nonnull UIScrollView *)scrollView
{
NSLog(@"%s", __func__);
} // 缩放结束时调用
- (void)scrollViewDidEndZooming:(nonnull UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale
{
NSLog(@"%s", __func__);
}

五、设置分页


 #import "ViewController.h"

 #define IMAGE_COUNT 5
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *sc; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; self.sc.showsHorizontalScrollIndicator = NO;
self.sc.showsVerticalScrollIndicator = NO; CGFloat width = self.sc.frame.size.width;
CGFloat height = self.sc.frame.size.height; // 1.初始化子控件, 添加图片
for (int i = ; i < IMAGE_COUNT; i++) {
// 1.创建UIImageView
UIImageView *iv = [[UIImageView alloc] init];
// 2.创建图片
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"img_%02i", i + ]];
// 3.设置每个UIImageView的frame
// iv.frame = CGRectMake(i * width, 0, width, height); // 按照宽度分页
iv.frame = CGRectMake(, i * height, width, height); // 按照高度分页
iv.image = image;
// 4.添加到父控件
[self.sc addSubview:iv];
} // 2.设置滚动范围
// self.sc.contentSize = CGSizeMake(IMAGE_COUNT * width, height);
self.sc.contentSize = CGSizeMake(width, IMAGE_COUNT * height);
self.sc.bounces = NO;
self.sc.pagingEnabled = YES;
// pagingEnabled实现分页的本质, 是按照UIScrollView的宽度或者高度来分页的
// UIScrollView的宽度就是一页的宽度
}
@end

要实现动态修改页码, 有两种方式
1.实时计算

 // 只要滚动就会调用
- (void)scrollViewDidScroll:(nonnull UIScrollView *)scrollView
{
// 1.计算页码
// 当前页码 = 偏移位 / UIScrollView的宽度
CGFloat page = scrollView.contentOffset.x / scrollView.frame.size.width;
int currnetPage = page + 0.5; // 2.修改页码
self.pageControl.currentPage = currnetPage;
}

2.翻页之后再计算
  2.1停止拖拽
  2.2停止减速

// 停止拖拽
- (void)scrollViewDidEndDragging:(nonnull UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (decelerate == NO) {
[self scrollViewDidEndDecelerating:scrollView];
}
}
// 停止减速
- (void)scrollViewDidEndDecelerating:(nonnull UIScrollView *)scrollView
{
// 1.计算页码
// 当前页码 = 偏移位 / UIScrollView的宽度
int page = scrollView.contentOffset.x / scrollView.frame.size.width;
NSLog(@"page = %i", page); // 2.修改页码
self.pageControl.currentPage = page;
}

点击UIpageControl进行翻页

    // 监听PageControl的点击事件
[self.pageControl addTarget:self action:@selector(pageControlClick:) forControlEvents:UIControlEventValueChanged]; - (IBAction)pageControlClick:(UIPageControl *)sender
{
NSLog(@"%lu", sender.currentPage);
self.sc.contentOffset = CGPointMake(sender.currentPage * self.sc.frame.size.width , );
}

让UIScrollView每隔一段事件就切换一页

 // scheduledTimerWithTimeInterval: 创建一个定时器, 并且立即可是计时
// TimeInterval: 间隔时间
// target: 调用谁的方法
// selector: 调用什么方法
// userInfo: 需要传递什么参数
// repeats: 是否重复
// 每隔2.0秒调用一次self的nextPage方法, 并且不传递任何参数
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES]; // 切换到下一页
- (void)nextPage
{
// 1.获取下一页的页码
NSUInteger page = self.pageControl.currentPage + ;
NSLog(@"%lu", self.pageControl.currentPage);
// 2.判断页码是否越界
if (page >= IMAGE_COUNT) {
// 如果越界就回到第0页
self.pageControl.currentPage = ;
}else
{
// 如果没有越界, 就进入到下一页
self.pageControl.currentPage = page;
} [self pageControlClick:self.pageControl];
} // 如果给userInfo赋值, 那么定时器调用的方法就必须接受参数, 并且接受的参数就是NSTimer
// 只要调用scheduled方法创建一个NSTimer对象, 系统就会自动将NSTimer添加到主线程中

如果是单线程,并且有多个任务,比如说添加一个Text View,在点击Text View时定时器会停止工作,那么需要做以下操作让主线程空出时间来执行定时器

- (void)startTimer
{
// 打开定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage:) userInfo:@"lnj" repeats:YES]; // 主线程在处理其它事件的时候, 分一点时间来处理NSTimer
// 1.0 0.1
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
} - (void)stopTimer
{
// 关掉定时器
#warning 注意:NSTimer是一次性的, 只要invalidate之后就不能使用了
// 只要调用invalidate方法, 系统就会将NSTimer从主线程移除, 并且销毁NSTimer对象
[self.timer invalidate]; }

五、图片轮播器


   

实现代码如下

 #import "ViewController.h"
#import "XMGPageView.h" @interface ViewController ()<UIScrollViewDelegate> @property(nonatomic, strong)XMGPageView *pageView;
@end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
/*
1.利用UIScrollView实现商品展示
2.用纯代码封装图片轮播器
*/ // 1.创建图片轮播器
XMGPageView *pageView = [XMGPageView pageView];
// 2.设置图片轮播器的frame
pageView.imageNames = @[@"img_01", @"img_02", @"img_03", @"img_04", @"img_05"];
pageView.frame = CGRectMake(, , , );
// pageView.frame = CGRectMake(0, 97, 330, 200);
[self.view addSubview:pageView];
self.pageView = pageView; } @end /***************华丽的分割线*******************/ #import <UIKit/UIKit.h> @interface XMGPageView : UIView + (instancetype)pageView; /** 所有需要展示的图片名称*/
@property (nonatomic, strong)NSArray *imageNames;
@end #import "XMGPageView.h" @interface XMGPageView ()<UIScrollViewDelegate> @property (weak, nonatomic) IBOutlet UIScrollView *sc;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl; // 注意:NSTimer应该是weak
@property (weak, nonatomic) NSTimer *timer;
@end @implementation XMGPageView + (instancetype)pageView
{
return [[[NSBundle mainBundle] loadNibNamed:@"XMGPageView" owner:nil options:nil] lastObject];
}
/*
自定义View的步骤:
1.重写初始化方法 (在里面进行一次性的初始化)
xib :awakeFromNib
纯代码:initWithFrame
2.重写layoutSubviews, 在里面布局子控件
3.接收外界传入的数据, 重写set方法
*/ - (void)awakeFromNib
{ self.sc.delegate = self;
// 1.隐藏滚动条
self.sc.showsHorizontalScrollIndicator = NO;
self.sc.showsVerticalScrollIndicator = NO; // 2.设置UIScrollView的其它属性
self.sc.bounces = NO;
self.sc.pagingEnabled = YES; // 3.监听PageControl的点击事件
[self.pageControl addTarget:self action:@selector(pageControlClick:) forControlEvents:UIControlEventValueChanged]; // 4.通过KVC给UIPageControl的私有属性赋值, 设置自定义图片
[self.pageControl setValue:[UIImage imageNamed:@"current"] forKeyPath:@"_currentPageImage"];
[self.pageControl setValue:[UIImage imageNamed:@"other"] forKeyPath:@"_pageImage"]; // 5.让UIScrollView每隔一段事件就切换一页
[self startTimer];
} #pragma mark - 内部监听
- (IBAction)pageControlClick:(UIPageControl *)sender
{ self.sc.contentOffset = CGPointMake(sender.currentPage * self.sc.frame.size.width , );
} #pragma mark - 定时器相关
- (void)startTimer
{
// 打开定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage:) userInfo:@"lnj" repeats:YES]; // 主线程在处理其它事件的时候, 分一点时间来处理NSTimer
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
} // 切换到下一页
- (void)nextPage:(NSTimer *)timer
{
// 1.获取下一页的页码
NSUInteger page = self.pageControl.currentPage + ;
// 2.判断页码是否越界
if (page >= _imageNames.count) {
// 如果越界就回到第0页
self.pageControl.currentPage = ;
}else
{
// 如果没有越界, 就进入到下一页
self.pageControl.currentPage = page;
} [self pageControlClick:self.pageControl];
} - (void)stopTimer
{
// 关掉定时器
[self.timer invalidate];
} #pragma mark - UIScrollViewDelegate
// 只要滚动就会调用
- (void)scrollViewDidScroll:(nonnull UIScrollView *)scrollView
{
// 1.计算页码
// 当前页码 = 偏移位 / UIScrollView的宽度
CGFloat page = scrollView.contentOffset.x / scrollView.frame.size.width;
int currnetPage = page + 0.5; // 2.修改页码
self.pageControl.currentPage = currnetPage;
} // 开始拖拽
- (void)scrollViewWillBeginDragging:(nonnull UIScrollView *)scrollView
{
[self stopTimer];
} // 结束拖拽
- (void)scrollViewDidEndDragging:(nonnull UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[self startTimer]; } - (void)setImageNames:(NSArray *)imageNames
{
_imageNames = imageNames; // 0.每次重新设置图片, 都需要清空以前的图片
for (UIView *subView in self.sc.subviews) {
[subView removeFromSuperview];
} // 1.初始化子控件, 添加图片
for (int i = ; i < _imageNames.count; i++) { // 1.创建UIImageView
UIImageView *iv = [[UIImageView alloc] init]; // 2.创建图片
NSString *imageName = _imageNames[i];
UIImage *image = [UIImage imageNamed:imageName];
iv.image = image; // 3.添加到父控件
[self.sc addSubview:iv];
} // 2.设置pageControl的页码数量
self.pageControl.numberOfPages = _imageNames.count; } - (void)layoutSubviews
{
[super layoutSubviews]; CGFloat width = self.sc.frame.size.width;
CGFloat height = self.sc.frame.size.height;
NSUInteger imageCount = self.imageNames.count;
// 1.设置每个UIImageView的frame
for (int i = ; i < imageCount; i++) {
UIImageView *iv = self.sc.subviews[i];
iv.frame = CGRectMake(i * width, , width, height);
} // 2.设置滚动范围
self.sc.contentSize = CGSizeMake(imageCount * width, height);
} @end

iOS开发——UI基础-UIScrollView的更多相关文章

  1. iOS开发UI篇—UIScrollView控件实现图片缩放功能

    iOS开发UI篇—UIScrollView控件实现图片缩放功能 一.缩放 1.简单说明: 有些时候,我们可能要对某些内容进行手势缩放,如下图所示 UIScrollView不仅能滚动显示大量内容,还能对 ...

  2. iOS开发UI篇—UIScrollView控件介绍

    iOS开发UI篇—UIScrollView控件介绍 一.知识点简单介绍 1.UIScrollView控件是什么? (1)移动设备的屏幕⼤大⼩小是极其有限的,因此直接展⽰示在⽤用户眼前的内容也相当有限 ...

  3. iOS开发UI篇—UIScrollView控件实现图片轮播

    iOS开发UI篇—UIScrollView控件实现图片轮播 一.实现效果 实现图片的自动轮播            二.实现代码 storyboard中布局 代码: #import "YYV ...

  4. iOS开发UI基础—手写控件,frame,center和bounds属性

    iOS开发UI基础—手写控件,frame,center和bounds属性 一.手写控件 1.手写控件的步骤 (1)使用相应的控件类创建控件对象 (2)设置该控件的各种属性 (3)添加控件到视图中 (4 ...

  5. 【转】 iOS开发UI篇—UIScrollView控件实现图片轮播

    原文:http://www.cnblogs.com/wendingding/p/3763527.html iOS开发UI篇—UIScrollView控件实现图片轮播 一.实现效果 实现图片的自动轮播 ...

  6. IOS开发UI基础--数据刷新

    IOS开发UI基础--数据刷新 cell的数据刷新包括下面几个方面 加入数据 删除数据 更改数据 全局刷新方法(最经常使用) [self.tableView reloadData]; // 屏幕上的全 ...

  7. iOS开发-UI基础Demo

    现在更多的学习资料都是xCode4.X的,发现xCode6.1还是很多东西,如果有正在学习iOS开发的可以通过Demo简单了解下iOS的UI开发~ 1.新建单视图文件: 2.新建项目名称,语言选择OC ...

  8. IOS开发UI基础之UIScrollView

    什么是UIScrollView ● 移动设备的屏幕⼤大⼩小是极其有限的,因此直接展⽰示在⽤用户眼前的内容也相当有限 ● 当展⽰示的内容较多,超出⼀一个屏幕时,⽤用户可通过滚动⼿手势来查看屏幕以外的内容 ...

  9. iOS开发——UI基础-屏幕适配

    一.适配 1.什么是适配?适应.兼容各种不同的情况 2.移动开发中,适配的常见种类 2.1系统适配 针对不同版本的操作系统进行适配 2.2屏幕适配 针对不同大小的屏幕尺寸进行适配 二.点和像素 1.在 ...

随机推荐

  1. How to set up an FTP server on Ubuntu 14.04

    How to set up an FTP server on Ubuntu 14.04 Setting up a fully-functional and highly secure FTP serv ...

  2. Mybatis的mapper文件中$和#的区别

    一般来说,我们使用mybatis generator来生成mapper.xml文件时,会生成一些增删改查的文件,这些文件中需要传入一些参数,传参数的时候,我们会注意到,参数的大括号外面,有两种符号,一 ...

  3. php 数据访问(以mysql数据库为例)

    //建一个连接,造一个连接对象$db = new MySQLi("localhost","root","123","mydb&qu ...

  4. WinForm------Reflector反编译工具下载

    地址: http://www.ddooo.com/softdown/70642.htm

  5. 10月17日下午MySQl数据库CRUD高级查询

    高级查询:1.连接查询 #适用于有外键关系的  没有任何关系没法用select * from Info,Nation #同时查询这俩表并把两表每个数据相互组合,形成笛卡尔积 select * from ...

  6. Jasper(物联网网络支撑平台公司)的技术为什么这么牛逼?

    Jasper在这个行业积累了十几年,合作的运营商超过30个,合作的行业大咖包括了通用.空客.宝马.特斯拉等几千个行业龙头,还是有很多积累下来的优势的. 一是,Jasper通过积累下来的行业应用经验,针 ...

  7. php瀑布流,把一个数组分4个数组,按照时间排序

    简单介绍:把一个数组分成4个数组,取其中1的倍数 <?php $arr = array( ', ', ', ', ', ', ', ', ', ', ', ', ', ); foreach($a ...

  8. Robot Framework--10 万能的evaluate

    转自:http://blog.csdn.net/tulituqi/article/details/10124559 这一讲我们重点来介绍一下一个常用的关键字evaluate. 我觉得这个关键字在RF里 ...

  9. Robot Framework--07 变量的声明、赋值及其使用

    转自:http://blog.csdn.net/tulituqi/article/details/7984642 一.变量的声明 1.变量标识符 每个变量都可以用  变量标识符{变量名}    来进行 ...

  10. centos 7.0 phpize 扩展php

    phpize扩展php模块 phpize 所在目录 /usr/etc/php/bin/phpize 查看当前php配置情况 /usr/etc/php/bin/下面的php [root@localhos ...