美文网首页
iOS UIView的frame和bounds学习笔记

iOS UIView的frame和bounds学习笔记

作者: HoooChan | 来源:发表于2019-02-02 13:42 被阅读41次

    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

    相关文章

      网友评论

          本文标题:iOS UIView的frame和bounds学习笔记

          本文链接:https://www.haomeiwen.com/subject/fyjssqtx.html