iOS UIView的frame和bounds学习笔记

frame是View在其父视图的坐标系中的位置;
bounds是View在其本身的坐标系中的位置。

当我们修改一个View的bounds时,View在其本身的坐标系中的位置发生了改变,但是其子View在该坐标系中的位置没有改变,所以它的子View和它本身的相对位置发生了改变,相当于子View移动了,但是子View的frame并没有改变。

UIScrollView滚动时,实际上就是修改它自己的bounds。我们可以创建一个UIScrollView的子类,然后在layoutSubviews方法中打印它的bounds:

- (void)layoutSubviews {
    [super layoutSubviews];
    NSLog(@"%@", NSStringFromCGRect(self.bounds));
}

可以发现滚动的时候UIScrollView的bounds一直在改变:

NestedScrollerView[4762:151333] {{0, 132}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 132.33333333333334}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 132.66666666666666}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 133}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 133.33333333333334}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 133.66666666666666}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 134}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 133.66666666666666}, {414, 736}}
NestedScrollerView[4762:151333] {{0, 133.33333333333334}, {414, 736}}

我们可以自定义实现一个ScrollView:

//
//  HOScrollView.m
//  HOScrollView
//
//  Created by HoChan on 2019/2/2.
//  Copyright © 2019 Okhoochan. All rights reserved.
//

#import "HOScrollView.h"
#import <pop/POP.h>

@interface HOScrollView()
@property (nonatomic, assign) CGRect startBounds;

@end

@implementation HOScrollView

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

- (void)handlePanGesture:(UIPanGestureRecognizer *)panGesture {
    switch (panGesture.state) {
        case UIGestureRecognizerStateBegan: {
            [self pop_removeAnimationForKey:@"bounce"];
            [self pop_removeAnimationForKey:@"decelerate"];
            self.startBounds = self.bounds;
            break;
        }
        case UIGestureRecognizerStateChanged: {
            CGPoint translation = [panGesture translationInView:self];
            
            CGRect bounds = self.startBounds;
            
            CGFloat newBoundsOriginX = self.startBounds.origin.x - translation.x;
            CGFloat minBoundsOriginX = 0;
            CGFloat maxBoundsOriginX = self.contentSize.width - self.bounds.size.width;
            CGFloat constrainedBoundsOriginX = fmax(minBoundsOriginX, fmin(maxBoundsOriginX, newBoundsOriginX));
            bounds.origin.x = constrainedBoundsOriginX + (newBoundsOriginX - constrainedBoundsOriginX) / 2.0;
            
            CGFloat newBoundsOriginY = self.startBounds.origin.y - translation.y;
            CGFloat minBoundsOriginY = 0;
            CGFloat maxBoundsOriginY = self.contentSize.height - self.bounds.size.height;
            CGFloat constrainedBoundsOriginY = fmax(minBoundsOriginY, fmin(maxBoundsOriginY, newBoundsOriginY));
            bounds.origin.y = constrainedBoundsOriginY + (newBoundsOriginY - constrainedBoundsOriginY) / 2.0;
            
            self.bounds = bounds;
            break;
        }
        case UIGestureRecognizerStateEnded: {
            CGPoint velocity = [panGesture velocityInView:self];
            velocity.x = -velocity.x;
            velocity.y = -velocity.y;
            
            POPDecayAnimation *decayAnimation = [POPDecayAnimation animationWithPropertyNamed:kPOPViewBounds];
            decayAnimation.velocity = [NSValue valueWithCGRect:CGRectMake(velocity.x, velocity.y, 0, 0)];
            [self pop_addAnimation:decayAnimation forKey:@"decelerate"];
            break;
        }
        default:
            break;
    }
}

- (void)setBounds:(CGRect)bounds {
    [super setBounds:bounds];
    
    BOOL outsideBoundsMinimum = bounds.origin.x < 0.0 || bounds.origin.y < 0.0;
    BOOL outsideBoundsMaximum = bounds.origin.x > self.contentSize.width - self.bounds.size.width ||
    bounds.origin.y > self.contentSize.height - self.bounds.size.height;
    
    if (outsideBoundsMinimum || outsideBoundsMaximum) {
        POPDecayAnimation *decayAnimation = [self pop_animationForKey:@"decelerate"];
        if (decayAnimation) {
            CGPoint target = bounds.origin;
            
            target.x = fmin(fmax(target.x, 0.0), self.contentSize.width - bounds.size.width);
            target.y = fmin(fmax(target.y, 0.0), self.contentSize.height - bounds.size.height);
            
            POPSpringAnimation *springAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewBounds];
            springAnimation.velocity = decayAnimation.velocity;
            springAnimation.toValue = [NSValue valueWithCGRect:CGRectMake(target.x, target.y, bounds.size.width, bounds.size.height)];
            springAnimation.springBounciness = 0.1;
            springAnimation.springSpeed = 5.0;
            [self pop_addAnimation:springAnimation forKey:@"bounce"];
            
            [self pop_removeAnimationForKey:@"decelerate"];
        }
    }
    
}

@end

可以利用Fackbook的pop动画库实现bounce和减速效果。

参考:
Understanding UIScrollView
Replicating UIScrollView’s deceleration with Facebook Pop
Experimenting with Facebook’s open source animation framework POP
grp/CustomScrollView

猜你喜欢

转载自blog.csdn.net/weixin_33924220/article/details/86813053