IOS开发——UI基础-UIScrollView

一、UIScrollView使用的步骤


 

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

 1 @interface ViewController ()

 2 @property (weak, nonatomic) IBOutlet UIScrollView *scrollView;

 3 @end

 4 // 1.添加两个子控件到UIScrollView中

 5 // 一个控件没有设置frame, 默认x/y就是0

 6 UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];

 7 [self.scrollView addSubview:btn];

 8 

 9 UISwitch *sw = [[UISwitch alloc] init];

10 CGRect tempFrame = sw.frame;

11 tempFrame.origin.y = 150;

12 sw.frame = tempFrame;

13 [self.scrollView addSubview:sw];

14 

15 // 添加一个按钮

16 UIButton *customBtn = [[UIButton alloc] init];

17 customBtn.frame = CGRectMake(0, 0, 100, 100);

18 customBtn.backgroundColor = [UIColor redColor];

19 [customBtn setTitle:@"我是按钮" forState:UIControlStateNormal];

20 [customBtn setTitle:@"我是高亮" forState:UIControlStateHighlighted];

21 [customBtn setTitle:@"我是disabled状态" forState:UIControlStateDisabled];

22 //[customBtn addTarget:self action:@selector(customBtnClick) forControlEvents:UIControlEventTouchUpInside];

23 [self.scrollView addSubview:customBtn];

24 

25 // 注意: 如果想让UIScrollView进行滚动, 必须设置可以滚动的范围

26 // 设置scrollView的滚动范围为, frame的宽高 + 100

27 self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width + 100, self.scrollView.frame.size.height + 100);

 

二、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(100, 0);

// 注意点: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 ..

 1 @interface ViewController ()<UIScrollViewDelegate>

 2 @property (weak, nonatomic) IBOutlet UIScrollView *sc;

 3 

 4 @end

 5 

 6     self.sc.delegate = self;

 7 

 8 

 9 #pragma mark - UIScrollViewDelegate

10 // 只要成为了UIScrollView的代理, 遵守代理协议, 实现协议中的方法

11 // 当UIScrollView发生一些变化的时候, 系统就会自动调用这些代理方法

12 

13 // scrollViewDidScroll方法什么时候调用?

14 // 只要UIScrollView滚动了, 系统就会自动调用

15 - (void)scrollViewDidScroll:(UIScrollView *)scrollView

16 {

17     NSLog(@"%s", __func__);

18 }

19 

20 // 只要用户准备开始拖拽了就会调用

21 - (void)scrollViewWillBeginDragging:(nonnull UIScrollView *)scrollView

22 {

23     NSLog(@"%s", __func__);

24 }

25 

26 

27 // 用户已经结束拖拽, 代表用户已经松手了

28 // 系统调用了该方法并不代表着,UIScrollView已经停止滚动了

29 

30 // 每次调用 停止拖拽方法时 ,系统都会传入一个当前是否有惯性的参数

31 // 我们可以判断该参数是否为YES, 如果是YES代表当前UIScrollView有惯性, 停止拖拽并不会停止滚动, 需要在停止减速方法中监听什么时候真正的停止

32 - (void)scrollViewDidEndDragging:(nonnull UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

33 {

34     NSLog(@"%s", __func__);

35     if (decelerate == NO) {

36 //        NSLog(@"没有惯性, 可以在当前方法监听UIScrollView是否停止滚动");

37         [self scrollViewDidEndDecelerating:scrollView];

38     }else{

39 //        NSLog(@"有惯性, 需要在减速结束方法中监听UIScrollView是否停止滚动");

40     }

41 }

42 

43 // UIScrollView已经停止减速了

44 // 只有执行了这个方法才代表UIScrollView已经停止滚动了

45 - (void)scrollViewDidEndDecelerating:(nonnull UIScrollView *)scrollView

46 {

47     NSLog(@"UIScrollView停止滚动了");

48 }

 

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

 

四、缩放图片


 

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

 1 // 要想缩放, 除了告诉UISrollView要缩放哪一个控件以外, 还要告诉UISrollView最小能缩多小, 最大能放多大

 2 self.sc.maximumZoomScale = 2.0;

 3 self.sc.minimumZoomScale = 0.5;

 4 

 5 

 6 // 因为所有的子控件都是我们添加进去的, 所以要缩放哪一个我们最清楚

 7 // 所以只要让控制器成为UISrollView的代理, 当UISrollView不清楚要缩放哪一个控件的时候

 8 // UISrollView就会调用它的代理方法, 问问代理到底要缩放哪一个

 9 self.sc.delegate = self;

10     

11     

12 // 因为UISrollView中可能有多个子控件

13 // 那么UISrollView就搞不清楚到底要缩放哪一个子控件

14 // 想要缩放, 必须明确的告诉UISrollView要缩放哪一个控件

15 // 在此方法中告诉UISrollView要缩放哪一个控件

16 - (nullable UIView *)viewForZoomingInScrollView:(nonnull UIScrollView *)scrollView

17 {

18     return self.iv;

19 }

20     

21 // 缩放的过程中调用

22 // 和scrollViewDidScroll一样, 只要缩放一点点就会调用

23 - (void)scrollViewDidZoom:(nonnull UIScrollView *)scrollView

24 {

25     NSLog(@"%s", __func__);

26 }

27 

28 // 缩放结束时调用

29 - (void)scrollViewDidEndZooming:(nonnull UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale

30 {

31     NSLog(@"%s", __func__);

32 }

 

五、设置分页


 1 #import "ViewController.h"

 2 

 3 #define IMAGE_COUNT 5

 4 @interface ViewController ()

 5 @property (weak, nonatomic) IBOutlet UIScrollView *sc;

 6 

 7 @end

 8 

 9 @implementation ViewController

10 

11 - (void)viewDidLoad {

12     [super viewDidLoad];

13     

14     self.sc.showsHorizontalScrollIndicator = NO;

15     self.sc.showsVerticalScrollIndicator = NO;

16     

17     CGFloat width = self.sc.frame.size.width;

18     CGFloat height = self.sc.frame.size.height;

19     

20     // 1.初始化子控件, 添加图片

21     for (int i = 0; i < IMAGE_COUNT; i++) {

22         // 1.创建UIImageView

23         UIImageView *iv = [[UIImageView alloc] init];

24         // 2.创建图片

25         UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"img_%02i", i + 1]];

26         // 3.设置每个UIImageView的frame

27 //        iv.frame = CGRectMake(i * width, 0, width, height); // 按照宽度分页

28         iv.frame = CGRectMake(0, i * height, width, height); // 按照高度分页

29         iv.image = image;

30         // 4.添加到父控件

31         [self.sc addSubview:iv];

32     }

33     

34     // 2.设置滚动范围

35 //    self.sc.contentSize = CGSizeMake(IMAGE_COUNT * width, height);

36     self.sc.contentSize = CGSizeMake(width, IMAGE_COUNT * height);

37     self.sc.bounces = NO;

38     self.sc.pagingEnabled = YES;

39     // pagingEnabled实现分页的本质, 是按照UIScrollView的宽度或者高度来分页的

40     // UIScrollView的宽度就是一页的宽度

41 }

42 @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 , 0);

}

 

让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 + 1;

    NSLog(@"%lu", self.pageControl.currentPage);

    // 2.判断页码是否越界

    if (page >= IMAGE_COUNT) {

        // 如果越界就回到第0页

        self.pageControl.currentPage = 0;

    }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];



}

 

五、图片轮播器


 

IOS开发——UI基础-UIScrollView   IOS开发——UI基础-UIScrollView

 实现代码如下

  1 #import "ViewController.h"

  2 #import "XMGPageView.h"

  3 

  4 @interface ViewController ()<UIScrollViewDelegate>

  5 

  6 @property(nonatomic, strong)XMGPageView *pageView;

  7 @end

  8 

  9 @implementation ViewController

 10 

 11 - (void)viewDidLoad

 12 {

 13     [super viewDidLoad];

 14     /*

 15      1.利用UIScrollView实现商品展示

 16      2.用纯代码封装图片轮播器

 17      */

 18    

 19     // 1.创建图片轮播器

 20     XMGPageView *pageView = [XMGPageView pageView];

 21     // 2.设置图片轮播器的frame

 22     pageView.imageNames = @[@"img_01", @"img_02", @"img_03", @"img_04", @"img_05"];

 23     pageView.frame = CGRectMake(27, 97, 320, 128);

 24 //    pageView.frame = CGRectMake(0, 97, 330, 200);

 25     [self.view addSubview:pageView];

 26     self.pageView = pageView;

 27     

 28 }

 29 

 30 @end

 31 

 32 /***************华丽的分割线*******************/

 33 

 34 #import <UIKit/UIKit.h>

 35 

 36 @interface XMGPageView : UIView

 37 

 38 + (instancetype)pageView;

 39 

 40 /** 所有需要展示的图片名称*/

 41 @property (nonatomic, strong)NSArray *imageNames;

 42 @end

 43 

 44 

 45 

 46 #import "XMGPageView.h"

 47 

 48 @interface XMGPageView ()<UIScrollViewDelegate>

 49 

 50 @property (weak, nonatomic) IBOutlet UIScrollView *sc;

 51 @property (weak, nonatomic) IBOutlet UIPageControl *pageControl;

 52 

 53 // 注意:NSTimer应该是weak

 54 @property (weak, nonatomic) NSTimer *timer;

 55 @end

 56 

 57 @implementation XMGPageView

 58 

 59 

 60 + (instancetype)pageView

 61 {

 62     return [[[NSBundle mainBundle] loadNibNamed:@"XMGPageView" owner:nil options:nil] lastObject];

 63 }

 64 /*

 65  自定义View的步骤:

 66  1.重写初始化方法 (在里面进行一次性的初始化)

 67     xib :awakeFromNib

 68     纯代码:initWithFrame

 69  2.重写layoutSubviews, 在里面布局子控件

 70  3.接收外界传入的数据, 重写set方法

 71 */

 72 

 73 - (void)awakeFromNib

 74 {

 75     

 76     self.sc.delegate = self;

 77     // 1.隐藏滚动条

 78     self.sc.showsHorizontalScrollIndicator = NO;

 79     self.sc.showsVerticalScrollIndicator = NO;

 80     

 81     // 2.设置UIScrollView的其它属性

 82     self.sc.bounces = NO;

 83     self.sc.pagingEnabled = YES;

 84     

 85     // 3.监听PageControl的点击事件

 86     [self.pageControl addTarget:self action:@selector(pageControlClick:) forControlEvents:UIControlEventValueChanged];

 87     

 88     // 4.通过KVC给UIPageControl的私有属性赋值, 设置自定义图片

 89     [self.pageControl setValue:[UIImage imageNamed:@"current"] forKeyPath:@"_currentPageImage"];

 90     [self.pageControl setValue:[UIImage imageNamed:@"other"] forKeyPath:@"_pageImage"];

 91     

 92     // 5.让UIScrollView每隔一段事件就切换一页

 93     [self startTimer];

 94 }

 95 

 96 #pragma mark - 内部监听

 97 - (IBAction)pageControlClick:(UIPageControl *)sender

 98 {

 99     

100     self.sc.contentOffset = CGPointMake(sender.currentPage * self.sc.frame.size.width , 0);

101 }

102 

103 #pragma mark - 定时器相关

104 - (void)startTimer

105 {

106     // 打开定时器

107     self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage:) userInfo:@"lnj" repeats:YES];

108     

109     // 主线程在处理其它事件的时候, 分一点时间来处理NSTimer

110     [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

111 }

112 

113 // 切换到下一页

114 - (void)nextPage:(NSTimer *)timer

115 {

116     // 1.获取下一页的页码

117     NSUInteger page = self.pageControl.currentPage + 1;

118     // 2.判断页码是否越界

119     if (page >= _imageNames.count) {

120         // 如果越界就回到第0页

121         self.pageControl.currentPage = 0;

122     }else

123     {

124         // 如果没有越界, 就进入到下一页

125         self.pageControl.currentPage = page;

126     }

127     

128     [self pageControlClick:self.pageControl];

129 }

130 

131 - (void)stopTimer

132 {

133     // 关掉定时器

134     [self.timer invalidate];

135 }

136 

137 #pragma mark - UIScrollViewDelegate

138 // 只要滚动就会调用

139 - (void)scrollViewDidScroll:(nonnull UIScrollView *)scrollView

140 {

141     // 1.计算页码

142     // 当前页码 = 偏移位 / UIScrollView的宽度

143     CGFloat page = scrollView.contentOffset.x / scrollView.frame.size.width;

144     int currnetPage = page + 0.5;

145     

146     // 2.修改页码

147     self.pageControl.currentPage = currnetPage;

148 }

149 

150 // 开始拖拽

151 - (void)scrollViewWillBeginDragging:(nonnull UIScrollView *)scrollView

152 {

153     [self stopTimer];

154 }

155 

156 // 结束拖拽

157 - (void)scrollViewDidEndDragging:(nonnull UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

158 {

159     [self startTimer];

160     

161 }

162 

163 

164 - (void)setImageNames:(NSArray *)imageNames

165 {

166     _imageNames = imageNames;

167     

168     // 0.每次重新设置图片, 都需要清空以前的图片

169     for (UIView *subView in self.sc.subviews) {

170         [subView removeFromSuperview];

171     }

172 

173     // 1.初始化子控件, 添加图片

174     for (int i = 0; i < _imageNames.count; i++) {

175         

176         // 1.创建UIImageView

177         UIImageView *iv = [[UIImageView alloc] init];

178         

179         // 2.创建图片

180         NSString *imageName = _imageNames[i];

181         UIImage *image = [UIImage imageNamed:imageName];

182         iv.image = image;

183 

184         // 3.添加到父控件

185         [self.sc addSubview:iv];

186     }

187     

188     // 2.设置pageControl的页码数量

189     self.pageControl.numberOfPages = _imageNames.count;

190     

191 }

192 

193 - (void)layoutSubviews

194 {

195     [super layoutSubviews];

196     

197     CGFloat width = self.sc.frame.size.width;

198     CGFloat height = self.sc.frame.size.height;

199     NSUInteger imageCount = self.imageNames.count;

200     // 1.设置每个UIImageView的frame

201     for (int i = 0; i < imageCount; i++) {

202         UIImageView *iv = self.sc.subviews[i];

203         iv.frame = CGRectMake(i * width, 0, width, height);

204     }

205 

206     // 2.设置滚动范围

207     self.sc.contentSize = CGSizeMake(imageCount * width, height);

208 }

209 

210 @end

 

你可能感兴趣的:(uiscrollview)