Talk about uiscrollview


First, before we talk about uiscrollview, let’s first understand how coordinate systems work in UIKit.

Coordinate system

Because everyviewDefines its own coordinate system, which looks like this: the X axis points to the right and the Y axis points down (as shown in the following figure).

Talk about uiscrollview

Logical coordinate system png

However, it should be noted that this logical coordinate system is independent of the width and height of the view. It has no boundary and extends infinitely in four directions (infinite extension is not really infinite because the range of the coordinate system is limited by the size of the cgfloat data type. It is 32 bits on a 32-bit system and 64 bits on a 64 bit system). Now let’s lay out some views in the coordinate system. Each different colored rectangle represents a subview:

Talk about uiscrollview

Add a subview to the coordinate system

The actual code is as follows:

UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
redView.backgroundColor = [UIColor colorWithRed:0.815 green:0.007
    blue:0.105 alpha:1];

UIView *greenView = [[UIView alloc] initWithFrame:CGRectMake(150, 160, 150, 200)];
greenView.backgroundColor = [UIColor colorWithRed:0.494 green:0.827
    blue:0.129 alpha:1];

UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(40, 400, 200, 150)];
blueView.backgroundColor = [UIColor colorWithRed:0.29 green:0.564
    blue:0.886 alpha:1];

UIView *yellowView = [[UIView alloc] initWithFrame:CGRectMake(100, 600, 180, 150)];
yellowView.backgroundColor = [UIColor colorWithRed:0.972 green:0.905
    blue:0.109 alpha:1];

[mainView addSubview:redView];
[mainView addSubview:greenView];
[mainView addSubview:blueView];
[mainView addSubview:yellowView];

When it comes to views, we will talk about the concepts of boundaries and frames. Next, we will briefly talk about boundaries and frames.


For view in appleboundsIt is explained as follows:

A bounding rectangle that describes the position and size of the view in its own coordinate system.

A view can be considered a window or viewport that enters a rectangular area of a plane defined by its coordinate system. And the view bounds represents the position and size of the rectangle.

Suppose that the width and height of the bounds rectangle in our view are 320 x 480 points, and its origin is the default value (0, 0). The view becomes a viewport for the coordinate system plane, showing a small portion of the entire plane. Everything outside the boundary is still there, but it is hidden. In fact, child views outside the bounding rectangle will remain visible unless clipstobonds = = Yes (default is no). However, the view does not detect a touch beyond its boundaries.

Talk about uiscrollview

A view provides a viewport into a plane defined by its coordinate system. The bounding rectangle of the view describes the location and size of the visible area

Next, we will modify the origin of the bounding rectangle:

CGRect bounds = mainView.bounds;
bounds.origin = CGPointMake(0, 100);
mainView.bounds = bounds;

The origin of the bounding rectangle is now at(0, 100)So our scene looks like this:

Talk about uiscrollview

Changing the origin of the bounding rectangle is equivalent to moving the viewport.

It looks as if the view has moved down 100 points. In fact, this is related to its own coordinate system. View on screenactualThe position (more precisely, in its parent view) remains fixed and unchanged because it is determined by its frame.

The coordinates of the above pictures are the coordinate systems drawn for their own views.

Because the position of the view is fixed (from its own point of view), imagine the coordinate system plane as a canvas that we can drag, and the view as a transparent glass. Adjusting the origin of bounds is equivalent to moving the canvas so that another part of it is visible through the view. In this way, it can become as shown in the following figure:

Talk about uiscrollview

Changing the origin of the bounding rectangle is equivalent to moving the coordinate system in the opposite direction, while the position of the view remains fixed because its frame will not change

This is exactlyUIScrollViewHow it rolls. However, from the user’s perception, it seems that the sub views of the view are moving, although their positions in the view coordinate system (in other words, their frames) remain unchanged. We can also write a simple uiscrollview based on this principle (because the uiscrollview of the system involves momentum rolling, bouncing, rolling indicator, scaling and delegation methods, etc.).

Customize uiscrollview

A scrolling view does not need to constantly update the coordinates of its child views to scroll. All it has to do is adjust the origin of its boundary. This makes it trivial to implement a very simple scrolling view. We set up a gesture recognizer to detect the user’s Pan gesture. In response to the gesture, we use bounds to pan the view by dragging:

// CustomScrollView.h
@import UIKit;

@interface CustomScrollView : UIView

@property (nonatomic) CGSize contentSize;


// CustomScrollView.m
#import "CustomScrollView.h"

@implementation CustomScrollView

- (id)initWithFrame:(CGRect)frame
    self = [super initWithFrame:frame];
    if (self == nil) {
        return nil;
    UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc]
        initWithTarget:self action:@selector(handlePanGesture:)];
    [self addGestureRecognizer:gestureRecognizer];
    return self;

- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer
    CGPoint translation = [gestureRecognizer translationInView:self];
    CGRect bounds = self.bounds;

    // Translate the view's bounds, but do not permit values that would violate contentSize
    CGFloat newBoundsOriginX = bounds.origin.x - translation.x;
    CGFloat minBoundsOriginX = 0.0;
    CGFloat maxBoundsOriginX = self.contentSize.width - bounds.size.width;
    bounds.origin.x = fmax(minBoundsOriginX, fmin(newBoundsOriginX, maxBoundsOriginX));

    CGFloat newBoundsOriginY = bounds.origin.y - translation.y;
    CGFloat minBoundsOriginY = 0.0;
    CGFloat maxBoundsOriginY = self.contentSize.height - bounds.size.height;
    bounds.origin.y = fmax(minBoundsOriginY, fmin(newBoundsOriginY, maxBoundsOriginY));

    self.bounds = bounds;
    [gestureRecognizer setTranslation:CGPointZero inView:self];


Just like the real uiscrollview, our class has a property that contentsize must be set externally to define the range of scrollable areas. When we adjust the boundary, we ensure that only valid values are allowed. Such a running effect is as follows:

Talk about uiscrollview

Our custom scroll view is running. Note that it lacks momentum roll, bounce, and roll indicators.


A frame rectangle that describes the position and size of the view in its parent view coordinate system.

Because this article is about the understanding of uiscrollview, the frame and the difference between frame and bounds will be introduced in detail in the next article, and will be covered here.

Recommended Today

JS generate guid method

JS generate guid method Globally unique identification(GUID) is an algorithm generatedBinaryCount Reg128 bitsNumber ofidentifier , GUID is mainly used in networks or systems with multiple nodes and computers. Ideally, any computational geometry computer cluster will not generate two identical guids, and the total number of guids is2^128In theory, it is difficult to make two […]