UICollectionView和UIDynamicanimation结合使用

又是星期天了,今天说一个如何在UICollectionView中,使用UIDynamicanimation,做一个可以左右摆动的动画效果


首先,新建一个DynamicView的类,继承自UIView,在这个类中,我们封装了一个带有左右摆动的动画效果

#import "DynamicView.h"

@implementation DynamicView
{
    UIDynamicAnimator *_animator;
    UIPushBehavior *_pushBehavior;
    
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {

        self.backgroundColor = [UIColor clearColor];
        
        //挂件
        self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.bounds.origin.x + 30, self.bounds.origin.y + 20, self.bounds.size.width - 60, self.bounds.size.height - 40)];
        self.imageView.userInteractionEnabled = YES;
        [self addSubview:self.imageView];
        
        
        //花纹
        self.topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0 , self.bounds.size.width - 60, 50)];
        [self addSubview:self.topImageView];
        
        //love寄语
        self.love = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 60, 40)];
        self.love.center = CGPointMake(self.bounds.size.width / 2, self.topImageView.center.y );
        self.love.userInteractionEnabled = YES;
        self.love.image = [UIImage imageNamed:@"love"];
        [self addSubview:self.love];
        
        //添加滑动
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleView:)];
        [self.imageView addGestureRecognizer:pan];
        
        
        //KVO观察者模式
        [self.imageView addObserver:self forKeyPath:@"center" options:NSKeyValueObservingOptionNew context:nil];
        
        //加载动画
        [self applyDynamic];
    }
    return self;
}



- (void)applyDynamic {
    
    UIDynamicBehavior *behavior = [[UIDynamicBehavior alloc] init];
    
    CGPoint anchor = self.imageView.center;
    anchor.y -= 200;

    
    self.topImageView.center = anchor;
    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:self.imageView attachedToAnchor:anchor];
    [behavior addChildBehavior:attachment];

    
    //重力
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.imageView]];
    gravity.magnitude = 10;
    [behavior addChildBehavior:gravity];
    
    //物理引擎公有属性
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.imageView]];
    itemBehavior.elasticity = 1.0;
    itemBehavior.allowsRotation = NO;
    itemBehavior.resistance = 2.0f;
    [behavior addChildBehavior:itemBehavior];
    
    
    //将动画加入到contentView中
    _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self];
    [_animator addBehavior:behavior];
    
}


- (void)handleView:(UIPanGestureRecognizer *)pan {
    if (pan.state == UIGestureRecognizerStateBegan) {
        if (_pushBehavior) {
            [_animator removeBehavior:_pushBehavior];
        }
        //推力
        _pushBehavior = [[UIPushBehavior alloc] initWithItems:@[pan.view] mode:UIPushBehaviorModeContinuous];
        [_animator addBehavior:_pushBehavior];
    }
    
    CGFloat offset = [pan translationInView:self].x / 10.f;
    _pushBehavior.pushDirection = CGVectorMake(offset, 0);
    if (pan.state == UIGestureRecognizerStateEnded) {
        [_animator removeBehavior:_pushBehavior];
        _pushBehavior = nil;
    }
}


//画连线
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    //起点
    CGPoint anchor = self.topImageView.center;
    //终点
    CGPoint pointLeft = CGPointMake(self.imageView.frame.origin.x + 62, self.imageView.frame.origin.y + 12);
    CGPoint pointRight = CGPointMake(self.imageView.frame.origin.x + self.imageView.frame.size.width - 57, self.imageView.frame.origin.y + 10);
    
    CGContextMoveToPoint(context, anchor.x - 120, anchor.y - 15);
    CGContextAddLineToPoint(context, pointLeft.x, pointLeft.y);
    
    CGContextMoveToPoint(context, anchor.x + 120, anchor.y - 15);
    CGContextAddLineToPoint(context, pointRight.x, pointRight.y);
    CGContextSetLineWidth(context, 1.0f);
    [[UIColor blackColor] setStroke];
    
    CGContextSetAllowsAntialiasing(context, YES);
    CGContextSetShouldAntialias(context, YES);
    self.layer.shouldRasterize = YES;
    
    CGContextDrawPath(context, kCGPathFillStroke);
    
    
}


//刷新画线
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    [self setNeedsDisplay];
}

//移除KVO
- (void)dealloc {
    [self.imageView removeObserver:self forKeyPath:@"center" context:nil];
}

然后,新建一个cell,继承自 UICollectionViewCell;

#import "BookCoverCell.h"

@implementation BookCoverCell

{
    UIDynamicAnimator *_animator;
    UIPushBehavior *_pushBehavior;
}

//初始化
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        
        //设置contentView的背景颜色
        self.contentView.backgroundColor = [UIColor clearColor];
        
        self.viewDynamic = [[DynamicView alloc] initWithFrame:self.bounds];
        [self.contentView addSubview:self.viewDynamic];
        
    }
    return self;
}

@end


随后,新建一个布局,继承自UICollectionViewFlowLayout,对cell进行布局,可以左右滑动的cell

#import "BookCoverLayout.h"

@implementation BookCoverLayout

//这两个常量用来设置cell的尺寸;
static CGFloat PageWidth = 450;
static CGFloat PageHeight = 360;
//初始化方法
- (instancetype)init {
    self = [super init];
    if (self) {
        self.scrollDirection = UICollectionViewScrollDirectionHorizontal; //1 滚动方向
        self.itemSize = CGSizeMake(PageWidth, PageHeight); //2 cell的尺寸
        self.minimumInteritemSpacing = 10; //3 cell间最小间距
    }
    return self;
}

//重写prepareLayout方法;
//告诉Layout对象去更新当前的layout;
- (void)prepareLayout {
    [super prepareLayout];
    //滚动视图的比例
    //1.设置滚动视图的停止速度
    self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;

    //2.设置插入间隔区域;定义一个在scrollview被拽出一个contentOffset 的时候的一个空间
    self.collectionView.contentInset = UIEdgeInsetsMake(0,
                                                        self.collectionView.bounds.size.width / 2 - PageWidth / 2 ,
                                                        0,
                                                        self.collectionView.bounds.size.width / 2 - PageWidth / 2);
    
}

//设置每个cell的布局
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes *attributes in array) {
        CGRect frame = attributes.frame;
        CGFloat distance = 0;
        //计算间距
        distance = fabs(self.collectionView.contentOffset.x + self.collectionView.contentInset.left - frame.origin.x);
        //根据相互间距离,设置缩放比例
        CGFloat scale = 0.7 * MIN(MAX(1 - distance / (self.collectionView.bounds.size.width), 0.75), 1);
        attributes.transform = CGAffineTransformMakeScale(scale, scale);
    }
    return array;
}

//当collection View 的bounds发生改变时,能够重新计算cell属性,当滚动时,UICollectionView会改变它的bounds;
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}

//封面快速到位,当手指抬起时,停止的点
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
    CGPoint newOffset;
    //获取当前位置
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
    //计算总宽度
    CGFloat width = layout.itemSize.width + layout.minimumLineSpacing;
    //计算偏移量
    CGFloat offset = proposedContentOffset.x + self.collectionView.contentInset.left;
    
    if (velocity.x > 0) {
        //ceil(x) 返回一个更大的数
        offset = width * ceil(offset / width); //如果velocity.x > 0,向右滚动,根据(偏移量 /宽度),决定滚动到哪个封面
    } else if (velocity.x == 0) {
        //round(x) 返回一个四舍五入的值
        offset = width * round(offset / width); //如果velocity.x = 0,滑动不充足,仍旧是当前cell
    } else if (velocity.x < 0) {
        //floor(x) 取不大于x的最大整数
        offset = width *floor(offset / width); //如果velocity.x < 0,向左滚动
    }
    newOffset.x = offset - self.collectionView.contentInset.left;
    newOffset.y = proposedContentOffset.y; //总是相同
    return newOffset;
}

@end

最后,新建一个View,继承自 UICollectionView ,实现数据源代理

#import "CollectionVIew.h"

@implementation CollectionVIew

static NSString *const reuseIdentifier = @"cell";

- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout {
    self = [super initWithFrame:frame collectionViewLayout:layout];
    if (self) {
    self.dataSource = self;
    self.delegate = self;
    [self registerClass:[BookCoverCell class] forCellWithReuseIdentifier:reuseIdentifier];
    self.backgroundColor = [UIColor clearColor];
    }
    self.showsHorizontalScrollIndicator = NO;
    return self;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 5;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    BookCoverCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
    cell.viewDynamic.imageView.image = [UIImage imageNamed:@"bg2"];
    cell.viewDynamic.topImageView.image = [UIImage originalSizeImageWithPDFNamed:@"mybg4.pdf"];
    return cell;
}

@end
这样,所有的准备工作就做完了,我们在ViewController中,alloc一下,就OK了;

#import "ViewController.h"
#import "CollectionVIew.h"
#import "BookCoverCell.h"
#import "BookCoverLayout.h"
#define  kScreenWidth  [UIScreen mainScreen].bounds.size.width
#define  kScreenHeight [UIScreen mainScreen].bounds.size.height
@interface ViewController ()
@property (strong, nonatomic) CollectionVIew *collectionView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    BookCoverLayout *myLayout = [[BookCoverLayout alloc] init];
    self.collectionView = [[CollectionVIew alloc] initWithFrame:CGRectMake(0, kScreenHeight * 0.25, kScreenWidth, kScreenHeight * 0.55) collectionViewLayout:myLayout];
    [self.view addSubview:self.collectionView];
}
@end

demo下载地址:

http://download.csdn.net/download/et295394330/9122053

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值