一、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. Glusterfs分布式存储介绍(一)

    环境准备 1.centos6.8 系统的虚拟机(四台) 2.关闭iptables和SELinux 3.预装glusterfs软件包 yum install -y centos-release-glus ...

  2. gnuplot配置HOME目录

    http://blog.csdn.net/jspenliany/article/details/39828261 本人使用gnuplot绘图,使用console version的来进行处理的时候,经常 ...

  3. B450黑苹果之路(1)

    安装黑苹果,采取的是懒人版写入磁盘分区中,然后再安装1)从硬盘中分两个区,一个是未来使用的目标分区,一个是磁盘镜像区2)由于HFS+支持逻辑分区,所以两个分区都放逻辑分区上,分区不格式化3)使用硬盘助 ...

  4. python实现软件的注册功能(机器码+注册码机制)

    http://www.cnblogs.com/cquptzzq/p/5940583.html 一.前言: 目的:完成已有python图像处理工具的注册功能 功能:用户运行程序后,通过文件自动检测认证状 ...

  5. cpg数据库处理_找到未提取的pdf

    cpg数据库处理_找到未提取的pdf,存放于文件夹Chinese_undeal_pdfs move_unextracted_pdfs.py # -*- coding: utf-8 -*- " ...

  6. ansible加密命令

    ansible-vault用于配置文件加密,如编写的playbook配置文件中包含敏感信息,不希望其他人随意查看,ansible-valut可加密/解密这个配置文件,刚试了下也可以加密txt文档,猜想 ...

  7. js与php转换时间戳

    php时间:1368524732 js代码: function getLocalTime(nS) { return new Date(parseInt(nS) * 1000).toLocaleStri ...

  8. Linux笔记-常用的命令(15-3-30)

    vim命令参考: http://www.cnblogs.com/softwaretesting/archive/2011/07/12/2104435.html [1]解压文件格式的区别: *.tar  ...

  9. ASP.NET MVC使用Bootstrap系列(5)——创建ASP.NET MVC Bootstrap Helpers

    阅读目录 序言 内置的HTML Helpers 创建自定义的Helpers 使用静态方法创建Helpers 使用扩展方法创建Helpers 创建Fluent Helpers 创建自动闭合的Helper ...

  10. yii2 数据库操作(转)

    开始使用数据库首先需要配置数据库连接组件,通过添加 db 组件到应用配置实现("基础的" Web 应用是 config/web.php),DSN( Data Source Name ...