UIScrollView 滚动视图

前言

  • Objective-C

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

    1
    @available(iOS 2.0, *)		 public class UIScrollView : UIView, NSCoding
  • 移动设备的屏幕大小是极其有限的,因此直接展示在用户眼前的内容也相当有限。当展示的内容较多,超出一个屏幕时,用户可通过滚动手势来查看屏幕以外的内容。普通的 UIView 不具备滚动功能,不适合显示过多的内容,UIScrollView 是一个能够滚动的视图控件,可以用来展示大量的内容,并且可以通过滚动查看所有的内容。

  • UIScrollView 的用法很简单,将需要展示的内容添加到 UIScrollView 中,设置 UIScrollView 的 contentSize 属性,告诉 UIScrollView 所有内容的尺寸,也就是告诉它滚动的范围。超出 UIScrollView 边框的内容会被自动隐藏,用户可以用过手势拖动来查看超出边框并被隐藏的内容。

  • UIScrollView 不仅能滚动显示大量内容,还能对其内容进行缩放处理,也就是说,要完成缩放功能的话,只需要将需要缩放的内容添加到 UIScrollView 中。

  • 如果 UIScrollView 无法滚动,可能是以下原因:

    • 没有设置 contentSize
    • scrollEnabled = NO
    • 没有接收到触摸事件 userInteractionEnabled = NO
  • UIScrollView 的各种尺寸

    ScrollView5

1、UIScrollView 的创建

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(30, 60, [[UIScreen mainScreen] bounds].size.width - 60, 490)];

    // 将 scrollView 添加到屏幕
    [self.view addSubview:scrollView];

    // 向滚动视图中添加显示内容,将 imageView 添加到 scrollView,所有 UIView 子类都可以添加
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"13"]];
    [scrollView addSubview:imageView];

    // 设置滚动的范围大小,包含隐藏的部分,contentSize 的大小一般大于 frame 属性设置的可视区的大小
    scrollView.contentSize = imageView.bounds.size;
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let scrollView: UIScrollView = UIScrollView(frame: CGRectMake(30, 60, UIScreen.mainScreen().bounds.size.width - 60, 490))

    // 将 scrollView 添加到屏幕
    self.view.addSubview(scrollView)

    // 向滚动视图中添加显示内容,将 imageView 添加到 scrollView,所有 UIView 子类都可以添加
    let imageView: UIImageView = UIImageView(image: UIImage(named: "13"))
    scrollView.addSubview(imageView)

    // 设置滚动的范围大小,包含了隐藏的部分,contentSize 的大小一般大于 frame 属性设置的可视区的大小
    scrollView.contentSize = imageView.bounds.size
  • Storyboard

    • 在 Storyboard 上添加 Scroll View 控件,在 Scroll View 控件上添加其它控件,如 ImageView 控件。

      • 在 Storyboard 中设置的 Scroll View 控件背景在程序运行时才能显示出来。

        ScrollView2

    • 将 Scroll View 控件拖线到 View Controller 代码中,设置 contentSize 的大小。

      • Objective-C

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        @interface ViewController ()

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

        @end

        @implementation ViewController

        - (void)viewDidLoad {
        [super viewDidLoad];

        // 设置滚动的范围大小
        self.scrollView.contentSize = CGSizeMake(364, 364);
        }

        @end
    • 运行显示效果

      ScrollView3 ScrollView4

2、UIScrollView 的设置

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    // 设置滚动条的风格
    /*
    UIScrollViewIndicatorStyleDefault, // 灰色样式,默认
    UIScrollViewIndicatorStyleBlack, // 黑色样式
    UIScrollViewIndicatorStyleWhite // 白色样式
    */
    scrollView.indicatorStyle = UIScrollViewIndicatorStyleDefault;

    // 设置是否显示滚动条
    scrollView.showsHorizontalScrollIndicator = YES; // 水平方向
    scrollView.showsVerticalScrollIndicator = YES; // 垂直方向

    // 设置滚动的范围大小
    /*
    告诉 UIScrollView 所有内容的尺寸,也就是告诉它滚动的范围
    内容的大小包含了隐藏的部分,contentSize 的大小一般大于 frame 属性设置的可视区的大小
    某个值等于 0 时,则 UIScrollView 在此方向上不能滚动,如 CGSizeMake(0, 364),在 x 方向上不能滚动
    */
    scrollView.contentSize = CGSizeMake(364, 364);

    // 设置四周额外的滚动区域
    /*
    在 UIScrollView 的 4 周增加额外的滚动区域
    一般用来避免 scrollView 的内容被其他控件挡住
    UIEdgeInsets UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right)
    */
    scrollView.contentInset = UIEdgeInsetsMake(64, 20, 30, 10); // 设置偏移量
    /*
    用来表示 UIScrollView 滚动的位置
    其实就是内容左上角与 scrollView 左上角的间距值
    CGPointZero 相当于 CGPointMake(0, 0)
    */
    scrollView.contentOffset = CGPointMake(100, 200);
    [scrollView setContentOffset:CGPointZero animated:YES];

    // 获取偏移量
    CGPoint contentOffset = scrollView.contentOffset;

    // 获取子视图
    /*
    水平和垂直滚动条也是 scrollView 的子视图
    */
    NSArray *subviews = scrollView.subviews;

    // 获取显示内容的高度
    /*
    CGRectGetMaxY(CGRect rect) 自动计算最大 Y 坐标值
    注意 水平和垂直滚动条也是 scrollView 的子视图,会产生计算错误
    */
    // 由最后一个控件计算
    CGFloat contentH = lastView.frame.origin.y + lastView.frame.size.height;

    // 使用系统方法计算
    CGFloat contentH = CGRectGetMaxY(lastView.frame);

    // 设置点击状态栏能否滚动到画面最顶端
    /*
    也可以在协议方法中设置
    */
    scrollView.scrollsToTop = YES;

    // 设置是否允许手动滚动
    scrollView.scrollEnabled = YES;

    // 设置是否整页移动
    scrollView.pagingEnabled = NO;

    // 设置是否开启弹簧效果
    scrollView.bounces = YES;

    // 关闭下沉效果
    /*
    如果 viewController 在导航里,这个 viewController 的第一个子视图是 ScrollView 或其子类,
    系统会让 ScrollView 有个下沉的效果,有时这个效果会跟自己的代码冲突,通常会把它关掉
    */

    // 判断是否实现了下沉效果
    if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) {

    // 关闭下沉效果
    self.automaticallyAdjustsScrollViewInsets = NO;
    }

    // 设置缩放倍数
    /*
    需要遵守 <UIScrollViewDelegate> 协议,并实现 viewForZoomingInScrollView 协议方法
    如果是在模拟器中测试,需要按住 option 键再拖动内容
    */
    scrollView.maximumZoomScale = 3; // 放大倍数,
    scrollView.minimumZoomScale = 0.1; // 缩小倍数

    // 获取当前缩放倍数
    CGFloat zoomScale = scrollView.zoomScale;

    // 设置代理,需要遵守协议 <UIScrollViewDelegate>
    scrollView.delegate = self;
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    // 设置滚动条的风格
    /*
    case Default // 灰色样式,默认
    case Black // 黑色样式
    case White // 白色样式
    */
    scrollView.indicatorStyle = .Default

    // 设置是否显示滚动条
    scrollView.showsHorizontalScrollIndicator = true // 水平方向
    scrollView.showsVerticalScrollIndicator = true // 垂直方向

    // 设置滚动的范围大小
    /*
    告诉 UIScrollView 所有内容的尺寸,也就是告诉它滚动的范围
    内容的大小包含了隐藏的部分,contentSize 的大小一般大于 frame 属性设置的可视区的大小
    某个值等于 0 时,则 UIScrollView 在此方向上不能滚动,如 CGSizeMake(0, 364),在 x 方向上不能滚动
    */
    scrollView.contentSize = CGSizeMake(364, 364)

    // 设置四周额外的滚动区域
    /*
    在 UIScrollView 的 4 周增加额外的滚动区域
    一般用来避免 scrollView 的内容被其他控件挡住
    UIEdgeInsets UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right)
    */
    scrollView.contentInset = UIEdgeInsetsMake(64, 20, 30, 10)
    // 设置偏移量
    /*
    用来表示 UIScrollView 滚动的位置
    其实就是内容左上角与 scrollView 左上角的间距值
    CGPointZero 相当于 CGPointMake(0, 0)
    */
    scrollView.contentOffset = CGPointMake(100, 200)
    scrollView.setContentOffset(CGPointZero, animated: true)

    // 获取偏移量
    let contentOffset: CGPoint = scrollView.contentOffset

    // 获取子视图
    /*
    水平和垂直滚动条也是 scrollView 的子视图
    */
    let subviews: Array = scrollView.subviews

    // 获取显示内容的高度
    /*
    CGRectGetMaxY(CGRect rect) 自动计算最大 Y 坐标值
    注意 水平和垂直滚动条也是 scrollView 的子视图,会产生计算错误
    */
    // 由最后一个控件计算
    let contentH = lastView.frame.origin.y + lastView.frame.size.height

    // 使用系统方法计算
    let contentH = CGRectGetMaxY(lastView.frame)

    // 设置点击状态栏能否滚动到画面最顶端
    /*
    也可以在协议方法中设置
    */
    scrollView.scrollsToTop = true

    // 设置是否允许手动滚动
    scrollView.scrollEnabled = true

    // 设置是否整页移动
    scrollView.pagingEnabled = false

    // 设置是否开启弹簧效果
    scrollView.bounces = true

    // 关闭下沉效果
    /*
    如果 viewController 在导航里,这个 viewController 的第一个子视图是 ScrollView 或其子类,
    系统会让 ScrollView 有个下沉的效果,有时这个效果会跟自己的代码冲突,通常会把它关掉
    */

    // 判断是否实现了下沉效果
    if self.respondsToSelector(Selector("setAutomaticallyAdjustsScrollViewInsets:")) {

    // 关闭下沉效果
    self.automaticallyAdjustsScrollViewInsets = true
    }

    // 设置缩放倍数
    /*
    需要遵守 UIScrollViewDelegate 协议,并实现 viewForZoomingInScrollView 协议方法
    如果是在模拟器中测试,需要按住 option 键再拖动内容
    */
    scrollView.maximumZoomScale = 3 // 放大倍数
    scrollView.minimumZoomScale = 0.1 // 缩小倍数

    // 获取当前缩放倍数
    let zoomScale: CGFloat = scrollView.zoomScale

    // 设置代理,需要遵守协议 UIScrollViewDelegate
    scrollView.delegate = self

3、向 scrollView 添加图片集

3.1 添加少量图片

  • 每次添加一张图片的时候都会创建一个 UIImageView 对象,如果添加的图片过多或图片太大会占用大量的内存。

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #define WIDTH   [UIScreen mainScreen].bounds.size.width
    #define HEIGHT [UIScreen mainScreen].bounds.size.height

    // 设置 scrollView 的尺寸
    CGFloat w = WIDTH - 20;
    CGFloat h = HEIGHT - 40;

    // 设置图片数量
    int count = 5;

    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 30, w, h)];
    scrollView.pagingEnabled = YES;
    [self.view addSubview:scrollView];

    // 设置 contentSize,水平方向能滚动
    scrollView.contentSize = CGSizeMake(count * w, 0);

    // 添加少量图片
    for (int i = 0; i < count; i++) {

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(i * w, 0, w, h)];
    imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%d.jpg", i]];

    // 将 imageView 添加到 scrollView 上
    [scrollView addSubview:imageView];
    }
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    let WIDTH = UIScreen.mainScreen().bounds.size.width
    let HEIGHT = UIScreen.mainScreen().bounds.size.height

    // 设置 scrollView 的尺寸
    let w = WIDTH - 20
    let h = HEIGHT - 40

    // 设置图片数量
    let count: Int = 5

    let scrollView: UIScrollView = UIScrollView(frame: CGRectMake(10, 30, w, h))
    scrollView.pagingEnabled = true
    self.view.addSubview(scrollView)

    // 设置 contentSize,水平方向能滚动
    scrollView.contentSize = CGSizeMake(count * w, 0)

    // 添加少量图片
    for i in 0 ..< count {

    let imageView: UIImageView = UIImageView(frame: CGRectMake(CGFloat(i) * w, 0, w, h))
    imageView.image = UIImage(named: String(format: "%d.jpg", i))

    // 将 imageView 添加到 scrollView 上
    scrollView.addSubview(imageView)
    }

3.2 添加大量图片

  • 只创建 3 个 UIImageView 对象,向 scrollView 添加图片时复用这 3 个 UIImageView 对象。

  • Objective-C

    1
    null

4、UIScrollView 的协议方法

  • 需遵守协议 UIScrollViewDelegate,并设置代理

  • 设置代理方法:

    • 通过代码

      • Objective-C

        1
        // self 就是控制器
        self.scrollView.delegate = self;

        -

    • 通过 storyboard 拖线

      ScrollView6

  • Objective-C

    • 拖拽

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // 将要开始拖拽
      - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

      }

      // 将要结束拖拽
      - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

      }

      // 已经结束拖拽,decelerate 松手后 是否有惯性滚动 0:没有,1:有
      - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {

      }
    • 滚动

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 滚动过程中,只要滚动就会触发
      - (void)scrollViewDidScroll:(UIScrollView *)scrollView {

      }

      // 已经结束滚动,滚动动画停止时执行,代码改变时触发,也就是 setContentOffset 改变时
      - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {

      }
    • 惯性滚动

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 将要开始惯性滚动
      - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {

      }

      // 已经结束惯性滚动
      - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

      }
    • 滚到顶端

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // 设置点击状态栏时是否滚到顶端
      - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {

      return YES;
      }

      // 已经滚到顶端,点击状态栏时调用
      - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {

      }
    • 缩放

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // 设置被缩放的空间,一个 scrollView 中只能有一个子控件被缩放,如果有很多个子控件缩放时会引起错乱
      - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {

      return [scrollView.subviews[0] viewWithTag:100];
      }

      // 将要开始缩放
      - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {

      }

      // 已经结束缩放
      - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {

      }

      // 缩放过程中,只要缩放就会触发
      - (void)scrollViewDidZoom:(UIScrollView *)scrollView {

      }
  • Swift

    • 拖拽

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // 将要开始拖拽
      func scrollViewWillBeginDragging(scrollView: UIScrollView) {

      }

      // 将要结束拖拽
      func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

      }

      // 已经结束拖拽,decelerate 松手后 是否有惯性滚动 0:没有,1:有
      func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {

      }
    • 滚动

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 滚动过程中,只要滚动就会触发,只要滚动就会触发
      func scrollViewDidScroll(scrollView: UIScrollView) {

      }

      // 已经结束滚动,滚动动画停止时执行,代码改变时触发,也就是 setContentOffset 改变时
      func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {

      }
    • 惯性滚动

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 将要开始惯性滚动
      func scrollViewWillBeginDecelerating(scrollView: UIScrollView) {

      }

      // 已经结束惯性滚动
      func scrollViewDidEndDecelerating(scrollView: UIScrollView) {

      }
    • 滚到顶端

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // 设置点击状态栏时是否滚到顶端
      func scrollViewShouldScrollToTop(scrollView: UIScrollView) -> Bool {

      return true
      }

      // 已经滚到顶端,点击状态栏时调用
      func scrollViewDidScrollToTop(scrollView: UIScrollView) {

      }
    • 缩放

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // 设置被缩放的空间,一个 scrollView 中只能有一个子控件被缩放,如果有很多个子控件缩放时会引起错乱
      func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {

      return scrollView.subviews[0].viewWithTag(100)
      }

      // 将要开始缩放
      func scrollViewWillBeginZooming(scrollView: UIScrollView, withView view: UIView?) {

      }

      // 已经结束缩放
      func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) {

      }

      // 缩放过程中,只要缩放就会触发
      func scrollViewDidZoom(scrollView: UIScrollView) {

      }

5、Storyboard 中设置

5.1 在 Storyboard 场景中设置

5.1.1 Scroll View 设置

ScrollView1

Style 滚动条颜色类型
Scroll Indicators
– Shows Horizontal Indicator 显示水平滚动条
– Shows Vertical Indicator 显示垂直滚动条
Scrolling
– Scolling Enabled 可以滚动
– Paging Enabled 按页面滚动
– Direction Lock Enabled 滚动方向固定
Bounce
– Bounces 启动弹簧效果
– Bounce Horizontal 水平弹簧效果
– Bounce Vertical 垂直弹簧效果
Zoom 最小/最大缩放值
Touch
– Bounces Zoom
– Delays Content Touches
– Cancellable Content Touches
Keyboard 键盘设置

5.2 在 Storyboard 场景绑定的 Controller 中设置

  • 在 Storyboard 添加的 ScrollView 上需在其上添加子视图作为 ContentView,并添加子视图的约束,ScrollView 才能够计算 contentSize 的大小,子视图才能够滚动。

  • 若要在 Storyboard 的 ScrollView 上添加一个不随 ContentView 滚动的视图,需在 ViewController 上添加一个和 ScrollView 相同位置大小的视图,并设置不滚动视图相对于该视图的约束。

文章目录
  1. 1. 前言
  2. 2. 1、UIScrollView 的创建
  3. 3. 2、UIScrollView 的设置
  4. 4. 3、向 scrollView 添加图片集
    1. 4.1. 3.1 添加少量图片
    2. 4.2. 3.2 添加大量图片
  5. 5. 4、UIScrollView 的协议方法
  6. 6. 5、Storyboard 中设置
    1. 6.1. 5.1 在 Storyboard 场景中设置
      1. 6.1.1. 5.1.1 Scroll View 设置
    2. 6.2. 5.2 在 Storyboard 场景绑定的 Controller 中设置
隐藏目录