The starting

I have been doing development for some time. I experienced the excitement of completing the project for the first time and the boredom of calling the API of the system every day, so I came up with the idea of exploring the underlying implementation.

Thoughts on scrollView

We will be largely used in the iOS development scrollView this control, we use the tableView/collectionview textView all inherit from it. The frequent use of scrollView got me interested in its underlying implementation. How does it work? How do I implement a scrollView? After reading this blog, I’m sure you can implement a simple scrollView yourself.

Let’s first consider the following questions:

  • Who does scrollView inherit from, and how does it detect finger slides?

  • How does scrollView implement scrolling?

  • How are the various properties in the scrollView implemented? Such as contentSize/contentOffSet…

By solving the above problems step by step, we can implement our own scrollView.

Implement scrollView step by step

1. There is no doubt that we all know that scrollView inherits from UIView, and the detection of finger sliding should place a gesture recognition on the view. The implementation code is as follows:
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] init];
        [panGesture addTarget:self action:@selector(panGestureAction:)];
        [self addGestureRecognizer:panGesture];
    }
    return self;
}Copy the code
2. To solve the second problem, we must first understand the concepts of frame and bounds.

Referring to them, you know that frame is its position and size relative to the superview coordinate system, bounds position and size relative to its own coordinate system, and origin is generally (0,0). But what’s the use of bounds origin? What happens when you change it?

When we try to change the origin of the bounds, we find that the view itself does not change, but the position of its subviews does, why?? Actually, when we change the Origin of the bounds, the position of the view itself does not change, but since the origin value is based on its own coordinate system, we change the position of its own coordinate system. The frame of the child view is based on the frame of the superview. When we change the Origin of the superview bounds, the position of the child view changes. This is the key to implementing the scrollView.

Is that easy to understand? Based on this, it is easy to implement a simple, rudimentary version of scrollView as follows:

- (void)panGestureAction:(UIPanGestureRecognizer *)pan {// record the initial position when each slide starts if (pan.state == UIGestureRecognizerStateBegan) { self.startLocation = self.bounds.origin; NSLog(@"%@", NSStringFromCGPoint(self.startLocation)); } / / relative to the offset of the initial touch points if (pan) state = = UIGestureRecognizerStateChanged) {CGPoint point = [pan translationInView: self]. NSLog(@"%@", NSStringFromCGPoint(point)); CGFloat newOriginalX = self.startLocation.x - point.x; CGFloat newOriginalY = self.startLocation.y - point.y; CGRect bounds = self.bounds; bounds.origin = CGPointMake(newOriginalX, newOriginalY); self.bounds = bounds; }}Copy the code
3. Understanding the key points above, let’s do a simple optimization to our implementation of scrollView.

Using contentSize to limit the internal space of the scrollView, the code is as follows

if (newOriginalX < 0) { newOriginalX = 0; } else { CGFloat maxMoveWidth = self.contentSize.width - self.bounds.size.width; if (newOriginalX > maxMoveWidth) { newOriginalX = maxMoveWidth; } } if (newOriginalY < 0) { newOriginalY = 0; } else { CGFloat maxMoveHeight = self.contentSize.height - self.bounds.size.height; if (newOriginalY > maxMoveHeight) { newOriginalY = maxMoveHeight; }}Copy the code

The initial offset of scrollView is set through contentOffset. You already know how to set the offset? That’s right, we just set the Origin of the View’s own bounds as follows:

 - (void)setContentOffset:(CGPoint)contentOffset {
    _contentOffset = contentOffset;
    CGRect newBounds = self.bounds;
    newBounds.origin = contentOffset;
    self.bounds = newBounds;
 }Copy the code

Prevent scrollView child views from exceeding the scrollView

self.layer.masksToBounds = YES;Copy the code

conclusion

UIScrollView has many other powerful features, above we just finished a very simple scrollView, I will improve it if I have time in the future. Of course, if you are interested, you can completely extend it, download the address here. In the meantime, I will continue to explore the low-level implementation of other UIKit controls. Please keep watching!