UIScrollView 的基本使用

UIScrollView 基本使用

  • UIScrollView 的三个属性

    • contentSize 设置滚动区域,只有设置了滚动区域才能够滚动
    • contentOffset 设置滚动内容偏移,决定当前显示的内容
    • contentInset 设置滚动外框的偏移
  • UIScrollView 无法滚动原因

  • UIScrollView 设置弹簧效果 & 滚动指示器

常用属性演练

准备工作

  • 新建项目
  • ViewController 中实现以下代码,添加 scrollView
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupUI];
}

#pragma mark - 设置界面
- (void)setupUI {

    // 1. 创建 UIScrollView
    UIScrollView *sv = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    sv.backgroundColor = [UIColor blueColor];
    [self.view addSubview:sv];
}

@end
  • 添加 imageView
// 2. 添加 imageView
UIImage *image = [UIImage imageNamed:@"002"];
// initWithImage 方法创建的 imageView 会根据 image 的大小自动调整大小
UIImageView *iv = [[UIImageView alloc] initWithImage:image];

// 将 图像视图 添加到 滚动视图上
[sv addSubview:iv];

运行程序,会发现不会滚动,那么如何滚动呢?

探索头文件

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIScrollView : UIView <NSCoding>

/// The point at which the origin of the content view is offset from the origin of the scroll view.
/// 内容视图原点(origin)所在的偏移位置,相对于 scroll view 的 origin,默认是 CGPointZero
@property(nonatomic)         CGPoint                      contentOffset;                  // default CGPointZero
/// The size of the content view
/// 内容视图的大小,默认是 CGSizeZero
@property(nonatomic)         CGSize                       contentSize;                    // default CGSizeZero
/// The distance that the content view is inset from the enclosing scroll view.
/// 内容视图围绕(enclosing) scroll view 的距离,默认值是 UIEdgeInsetsZero
@property(nonatomic)         UIEdgeInsets                 contentInset;                   // default UIEdgeInsetsZero. add additional scroll area around content
  • 定义属性,方便后续代码演练
@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, weak) UIImageView *imageView;
  • setupUI 方法中使用成员变量记录局部变量
_scrollView = sv;
_imageView = iv;
  • 新建 demoScrollView 方法并在 viewDidLoad 方法中调用
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupUI];
    [self demoScrollView];
}

#pragma mark - 演练 scrollview
- (void)demoScrollView {

}

三个属性演练

  • contentSize
  • contentOffset
  • contentInset
contentSize
  • demoScrollView 中实现以下方法
- (void)demoScrollView {

    // 1. 设置 contentSize
    // 让 scrollView 的 contentSize 等于 图像视图的大小
    // 设置了滚动视图的 contentSize 之后,滚动视图就能够滚动了
    _scrollView.contentSize = _imageView.bounds.size;   
}

结论

设置了滚动视图的 contentSize 之后,滚动视图就能够滚动了

没有 contentSize,scrollView 就不知道要滚多远

  • 单独设置 contentSizewidth
// contentSize 的 width 决定了水平方向能滚多远
_scrollView.contentSize = CGSizeMake(_imageView.bounds.size.width, 0);
  • 单独设置 contentSizeheight
// contentSize 的 height 决定了垂直方向能滚多远
_scrollView.contentSize = CGSizeMake(0, _imageView.bounds.size.height);

结论

  • scrollView 要滚动就必须设置了滚动视图的 contentSize
  • contentSize 的 width 决定了水平方向滚动距离
  • contentSize 的 height 决定了垂直方向滚动距离
  • 方法名重构 —— 快捷键 cmd + shift + e
/// 演示 contentSize
///
/// 结论:
/// - scrollView 要滚动就必须设置了滚动视图的 contentSize
/// - contentSize 的 width 决定了水平方向滚动距离
/// - contentSize 的 height 决定了垂直方向滚动距离
- (void)demoContentSize {

    // 1. 设置 contentSize
    // 让 scrollView 的 contentSize 等于 图像视图的大小
    // 设置了滚动视图的 contentSize 之后,滚动视图就能够滚动了
    _scrollView.contentSize = _imageView.bounds.size;

    // contentSize 的 width 决定了水平方向能滚多远
//    _scrollView.contentSize = CGSizeMake(_imageView.bounds.size.width, 0);

    // contentSize 的 height 决定了垂直方向能滚多远
//    _scrollView.contentSize = CGSizeMake(0, _imageView.bounds.size.height);
}
contentOffset
  • 新建方法 demoContentOffset 并在 viewDidLoad 调用
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupUI];
    [self demoContentSize];
    [self demoContentOffset];
}

#pragma mark - 演练 scrollview
/// 演示 contentOffset
- (void)demoContentOffset {

}
  • 增加演示按钮
/// 演示 contentOffset
- (void)demoContentOffset {

    // 1. 增加演示按钮
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoLight];
    btn.center = self.view.center;
    [self.view addSubview:btn];

    [btn addTarget:self action:@selector(clickOffsetButton) forControlEvents:UIControlEventTouchUpInside];
}

/// 点击测试 offset 按钮
- (void)clickOffsetButton {

}
  • 实现代码修改 contentOffset
/// 点击测试 offset 按钮
- (void)clickOffsetButton {

    // 修改 scrollView 的 contentOffset
    _scrollView.contentOffset = CGPointMake(50, 50);

    // bounds 决定了内部控件布局的原点坐标
    NSLog(@"%@", NSStringFromCGRect(_scrollView.bounds));
}
  • 修改代码,递增 contentOffset 的变化
// 2> 递增 contentOffset
CGPoint p = _scrollView.contentOffset;
p.x += 50;
p.y += 50;
_scrollView.contentOffset = p;
  • 修改 NSLog
// bounds 决定了内部控件布局的原点坐标
// scrollView 的 contentOffset 属性本质上就是 bounds 的原点
NSLog(@"%@ - %@", NSStringFromCGRect(_scrollView.bounds), NSStringFromCGPoint(_scrollView.contentOffset));

结论

  • scrollView 通过修改 contentOffset 调整内部视图的坐标位置,从而给用户产生一种视觉上的滚动的效果
  • contentOffset 的值本质上就是 bounds 的原点(origin) 值,苹果在为了方便程序员的理解,增加了这个属性
  • 文档释义:contentOffset:内容视图原点(origin)所在的偏移位置,相对于 scroll view 的 origin,默认是 CGPointZero


contentOffset 相关方法
  • 探索头文件
/// 以恒定速度动画移动到新的 offset
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;  // animate at constant velocity to new offset
/// 滚动到可见区域(靠近边缘-不会滚动到边缘外侧),如果当前区域完全可见,则什么也不做
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated;         // scroll so rect is just visible (nearest edges). nothing if rect completely visible

苹果头文件的特点:越是重要的属性和方法就越靠上

  • 新建测试方法
/// 测试 offset 相关方法
- (void)testSetOffsetMethod {

}
  • 修改按钮监听方法
[btn addTarget:self action:@selector(testSetOffsetMethod) forControlEvents:UIControlEventTouchUpInside];
  • 实现方法,测试 setContentOffset:animated: 方法
/// 测试 offset 相关方法
- (void)testSetOffsetMethod {

    // 1. 测试 setContentOffset
    CGFloat x = arc4random_uniform(_imageView.bounds.size.width);
    CGFloat y = arc4random_uniform(_imageView.bounds.size.height);

    // 利用系统默认的动画效果,动画时长不能修改
    [_scrollView setContentOffset:CGPointMake(x, y) animated:YES];
}
  • 自定义动画效果
// 2> 自定义动画效果
[UIView
 animateWithDuration:1.0
 delay:0 usingSpringWithDamping:0.8
 initialSpringVelocity:0
 options:0
 animations:^{
     _scrollView.contentOffset = CGPointMake(x, y);
 } completion:nil];
  • 新建方法 testScrollRectMethod
- (void)testScrollRectMethod {

    // 传入当前完全可见区域,什么也不发生
    [_scrollView scrollRectToVisible:_scrollView.bounds animated:YES];
}
  • 随机区域
// 2> 随机区域
CGFloat x = arc4random_uniform(_imageView.bounds.size.width);
CGFloat y = arc4random_uniform(_imageView.bounds.size.height);
CGRect rect = CGRectMake(x, y, _scrollView.bounds.size.width, _scrollView.bounds.size.height);

[_scrollView scrollRectToVisible:rect animated:YES];
contentInset
/// 内容视图围绕(enclosing) scroll view 的距离,默认值是 UIEdgeInsetsZero
@property(nonatomic)         UIEdgeInsets                 contentInset;                   // default UIEdgeInsetsZero. add additional scroll area around content
  • 新增方法 demoContentInset 并且在 viewDidLoad 调用
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupUI];
    [self demoContentSize];
    [self demoContentOffset];
    [self demoContentInset];
}

#pragma mark - 演练 scrollview
/// 演示 contentInset
- (void)demoContentInset {
}
  • 实现方法 demoContentInset
/// 演示 contentInset
- (void)demoContentInset {

    UIEdgeInsets inset = UIEdgeInsetsMake(50, 50, 50, 50);

    // 边距设置了,但是初始没有效果,需要拖拽一下才有效果
    _scrollView.contentInset = inset;
}
  • 利用 contentOffset 设置初始位置
// 设置 contentOffset 调整到边距对应位置
_scrollView.contentOffset = CGPointMake(-inset.left, -inset.top);

结论

  • scrollView 通过修改 contentInset 调整内部和边缘的偏移

  • 设置边距之后,初始没有效果,需要拖拽一下才有效果

  • 可以通过设置 contentOffset 调整初始位置

scrollView 与内容相关的三个属性示意图如下:


结论

  • scrollView 要滚动就必须设置了滚动视图的 contentSize

    • contentSize 的 width 决定了水平方向滚动距离
    • contentSize 的 height 决定了垂直方向滚动距离
  • scrollView 通过修改 contentOffset 调整内部视图的坐标位置,从而给用户产生一种视觉上的滚动的效果

    • contentOffset 的值本质上就是 bounds 的原点(origin) 值,苹果在为了方便程序员的理解,增加了这个属性
  • scrollView 通过修改 contentInset 调整内部和边缘的偏移

    • 设置边距之后,初始没有效果,需要拖拽一下才有效果
    • 可以通过设置 contentOffset 调整初始位置
设置弹簧效果 & 滚动指示器
  • 探索头文件
/// 默认 YES
@property(nonatomic)         BOOL                         bounces;                        // default YES. if YES, bounces past edge of content and back again
/// 始终垂直弹,默认是 NO,如果设置成 YES,即使内容比区域小,同样允许垂直方向弹动
@property(nonatomic)         BOOL                         alwaysBounceVertical;           // default NO. if YES and bounces is YES, even if content is smaller than bounds, allow drag vertically
/// 始终水平弹,默认是 NO,如果设置成 YES,即使内容比区域小,同样允许水平方向弹动
@property(nonatomic)         BOOL                         alwaysBounceHorizontal;         // default NO. if YES and bounces is YES, even if content is smaller than bounds, allow drag horizontally

/// 是否允许滚动,默认是 YES,关闭之后禁止任何拖拽
@property(nonatomic,getter=isScrollEnabled) BOOL          scrollEnabled;                  // default YES. turn off any dragging temporarily
/// 显示水平滚动指示器
@property(nonatomic)         BOOL                         showsHorizontalScrollIndicator; // default YES. show indicator while we are tracking. fades out after tracking
/// 显示垂直滚动指示器
@property(nonatomic)         BOOL                         showsVerticalScrollIndicator;   // default YES. show indicator while we are tracking. fades out after tracking
/// 滚动指示器边距
@property(nonatomic)         UIEdgeInsets                 scrollIndicatorInsets;          // default is UIEdgeInsetsZero. adjust indicators inside of insets
  • 新建方法,并且在 viewDidLoad 调用
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupUI];
    [self demoContentSize];
    [self demoContentOffset];
    [self demoContentInset];

    [self demoBounces];
}

#pragma mark - 演练 scrollview
/// 演示弹簧效果
- (void)demoBounces {

}
  • 实现方法,禁止弹簧效果
/// 演示弹簧效果
- (void)demoBounces {
    // 1. 禁止弹簧效果
    _scrollView.bounces = NO;
}
  • 测试始终弹动
// 2. 测试始终允许弹簧效果
// 取消 contentSize 无法滚动
_scrollView.contentSize = CGSizeZero;
_scrollView.alwaysBounceVertical = YES;
_scrollView.alwaysBounceHorizontal = YES;
  • 测试禁止滚动属性
_scrollView.scrollEnabled = NO;

如果禁止滚动,弹簧效果同样失效

  • 测试滚动指示器
// 3. 滚动指示器
// 1> 禁用垂直滚动指示器
_scrollView.showsVerticalScrollIndicator = NO;
// 1> 禁用水平滚动指示器
_scrollView.showsHorizontalScrollIndicator = NO;

查看视图层次结构会发现,禁用指示器之后,那两个 UIImageView 不见了

结论

苹果是用 imageView 实现的水平垂直指示器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值