两个类~直接跑跑看撒~
//
// DLCardView.h// dielianw
//
// Created by 刘威 on 16/8/10.
// Copyright © 2016年 apple. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface DLCardView : UIView
@end
//
// DLCardView.m
// dielianw
//
// Created by 刘威 on 16/8/10.
// Copyright © 2016年 apple. All rights reserved.
//
#import "DLCardView.h"
#import "DLCardFrameModel.h"
#define ATTACHMENTVIEWHEIGHT (SCREEN_WIDTH - 80)
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
@interface DLCardView ()<UICollisionBehaviorDelegate>
@property (strong, nonatomic) UIDynamicAnimator *dynamicAnimator;
@property (strong, nonatomic) UIAttachmentBehavior *attachmentBehavior;
@property (strong, nonatomic) UISnapBehavior *snapBehavior;
@property (assign, nonatomic) CGPoint attachmentViewCenter;
@property (strong, nonatomic) UIView *attachmentView;
@property (strong, nonatomic) NSMutableArray *viewArray;
@property (strong, nonatomic) NSMutableArray *frameArray;
@property (strong, nonatomic) DLCardFrameModel *firstViewFrameModel;
@property (strong, nonatomic) DLCardFrameModel *lastViewFrameModel;
@end
@implementation DLCardView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_viewArray = [NSMutableArray array];
_frameArray = [NSMutableArray array];
_lastViewFrameModel = [[DLCardFrameModel alloc] initWithRotation:0
scale:1
x:SCREEN_WIDTH / 2
y:SCREEN_HEIGHT - 80 - ATTACHMENTVIEWHEIGHT / 2
hidden:NO];
DLCardFrameModel *model1 = [[DLCardFrameModel alloc] initWithRotation:-M_PI_2 / 8
scale:0.9
x:_lastViewFrameModel.x + 23 * (SCREEN_WIDTH / 320)
y:_lastViewFrameModel.y - 50 * (SCREEN_WIDTH / 320)
hidden:NO];
DLCardFrameModel *model2 = [[DLCardFrameModel alloc] initWithRotation:-M_PI_2 / 8 * 2
scale:0.8
x:model1.x + 15 * (SCREEN_WIDTH / 320)
y:model1.y - 50 * (SCREEN_WIDTH / 320)
hidden:NO];
DLCardFrameModel *model3 = [[DLCardFrameModel alloc] initWithRotation:-M_PI_2 / 8 * 3
scale:0.7
x:model2.x
y:model2.y - 50 * (SCREEN_WIDTH / 320)
hidden:NO];
DLCardFrameModel *model4 = [[DLCardFrameModel alloc] initWithRotation:-M_PI_2 / 8 * 4
scale:0.6
x:model3.x - 10 * (SCREEN_WIDTH / 320)
y:model3.y - 45 * (SCREEN_WIDTH / 320)
hidden:NO];
DLCardFrameModel *model5 = [[DLCardFrameModel alloc] initWithRotation:-M_PI_2 / 8 * 5
scale:0.5
x:model4.x - 20 * (SCREEN_WIDTH / 320)
y:model4.y - 35 * (SCREEN_WIDTH / 320)
hidden:NO];
DLCardFrameModel *model6 = [[DLCardFrameModel alloc] initWithRotation:-M_PI_2 / 8 * 6
scale:0.4
x:model5.x - 25 * (SCREEN_WIDTH / 320)
y:model5.y - 25 * (SCREEN_WIDTH / 320)
hidden:NO];
_firstViewFrameModel = [[DLCardFrameModel alloc] initWithRotation:-M_PI_2 / 8 * 7
scale:0.3
x:model6.x - 25 * (SCREEN_WIDTH / 320)
y:model6.y - 25 * (SCREEN_WIDTH / 320)
hidden:YES];
[_frameArray addObject:_firstViewFrameModel];
[_frameArray addObject:model6];
[_frameArray addObject:model5];
[_frameArray addObject:model4];
[_frameArray addObject:model3];
[_frameArray addObject:model2];
[_frameArray addObject:model1];
[_frameArray addObject:_lastViewFrameModel];
for (NSInteger i = 0; i < _frameArray.count; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake((SCREEN_WIDTH - ATTACHMENTVIEWHEIGHT) / 2, SCREEN_HEIGHT - 80 - ATTACHMENTVIEWHEIGHT, ATTACHMENTVIEWHEIGHT, ATTACHMENTVIEWHEIGHT)];
DLCardFrameModel *model = _frameArray[i];
view.center = model.point;
view.layer.masksToBounds = YES;
view.layer.cornerRadius = 20;
view.backgroundColor = [UIColor randomColor];
[self addSubview:view];
NSTimeInterval time = 0.4 - 0.02 * i;
[self setDynamicWithView:view model:model duration:time];
view.hidden = model.hidden;
if (i == _frameArray.count - 1) {
_attachmentView = view;
_attachmentView.userInteractionEnabled = YES;
}
[_viewArray addObject:view];
}
_attachmentViewCenter = _attachmentView.center;
_dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
[_attachmentView addGestureRecognizer:pan];
}
return self;
}
- (void)panAction:(UIPanGestureRecognizer *)panGesture {
CGPoint location = [panGesture locationInView:self];
CGPoint p = panGesture.view.center;
if (panGesture.state == UIGestureRecognizerStateBegan) {
[_dynamicAnimator removeAllBehaviors];
[_dynamicAnimator removeBehavior:_snapBehavior];
_snapBehavior = nil;
_attachmentBehavior = [[UIAttachmentBehavior alloc]initWithItem:panGesture.view offsetFromCenter:UIOffsetMake(-(p.x - location.x), -(p.y - location.y)) attachedToAnchor:location];
_attachmentBehavior.damping = 100;
_attachmentBehavior.length = 0;
[_dynamicAnimator addBehavior:_attachmentBehavior];
} else if (panGesture.state == UIGestureRecognizerStateChanged) {
_attachmentBehavior.anchorPoint = location;
double distance = sqrt(pow(_attachmentViewCenter.x - location.x, 2) + pow(_attachmentViewCenter.y - location.y, 2));
} else if (panGesture.state == UIGestureRecognizerStateEnded ||
panGesture.state == UIGestureRecognizerStateCancelled) {
[_dynamicAnimator removeBehavior:_attachmentBehavior];
[_dynamicAnimator removeBehavior:_snapBehavior];
[_dynamicAnimator removeAllBehaviors];
if (p.x < 120) {
CGVector directionVector = CGVectorMake((p.x - _attachmentViewCenter.x) * 0.8, p.y - _attachmentViewCenter.y);
UIPushBehavior *pushBehavior = [self pushBehaviorToPushView:_attachmentView direction:directionVector];
[_dynamicAnimator addBehavior:pushBehavior];
NSInteger angle;
if (location.y < p.y) {
angle = -5;
} else angle = 5;
UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[_attachmentView]];
itemBehavior.friction = 0.2;
itemBehavior.allowsRotation = YES;
[itemBehavior addAngularVelocity:angle forItem:_attachmentView];
[_dynamicAnimator addBehavior:itemBehavior];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self loadNextSubcardView:_attachmentView];
});
} else {
[_dynamicAnimator removeBehavior:_attachmentBehavior];
_attachmentBehavior = nil;
_snapBehavior = [self snapBehaviorFromSnapView:_attachmentView toPoint:_attachmentViewCenter];
[_dynamicAnimator addBehavior:_snapBehavior];
}
} else {
[_dynamicAnimator removeBehavior:_attachmentBehavior];
_attachmentBehavior = nil;
_snapBehavior = [self snapBehaviorFromSnapView:_attachmentView toPoint:_attachmentViewCenter];
[_dynamicAnimator addBehavior:_snapBehavior];
}
}
- (void)loadNextSubcardView:(UIView *)view {
[_viewArray removeObject:view];
[view removeFromSuperview];
view = nil;
UIView *lastView = _viewArray[_viewArray.count - 1];
_attachmentView = lastView;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
[_attachmentView addGestureRecognizer:pan];
UIView *newView = [[UIView alloc] initWithFrame:CGRectMake((SCREEN_WIDTH - ATTACHMENTVIEWHEIGHT) / 2, SCREEN_HEIGHT - 80 - ATTACHMENTVIEWHEIGHT, ATTACHMENTVIEWHEIGHT, ATTACHMENTVIEWHEIGHT)];
newView.layer.masksToBounds = YES;
newView.layer.cornerRadius = 20;
newView.backgroundColor = [UIColor randomColor];
[self addSubview:newView];
newView.userInteractionEnabled = NO;
newView.hidden = YES;
CGAffineTransform t = CGAffineTransformMakeRotation(_firstViewFrameModel.rotation);
newView.transform = CGAffineTransformScale(t, _firstViewFrameModel.scale, _firstViewFrameModel.scale);
[_viewArray insertObject:newView atIndex:0];
[_viewArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
UIView *view = _viewArray[idx];
DLCardFrameModel *model = _frameArray[idx];
NSTimeInterval time = 0.4 - 0.02 * idx;
[self setDynamicWithView:view model:model duration:time];
}];
_attachmentView.userInteractionEnabled = YES;
}
- (UIPushBehavior *)pushBehaviorToPushView:(UIView *)view direction:(CGVector)direction {
if (!view) {
return nil;
}
UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[view] mode:UIPushBehaviorModeInstantaneous];
pushBehavior.pushDirection = direction;
return pushBehavior;
}
- (UICollisionBehavior *)collisionBehaviorThatBoundsView:(UIView *)view inRect:(CGRect)rect {
if (!view) {
return nil;
}
UICollisionBehavior *collisionBehavior =
[[UICollisionBehavior alloc] initWithItems:@[ view ]];
UIBezierPath *collisionBound = [UIBezierPath bezierPathWithRect:rect];
[collisionBehavior addBoundaryWithIdentifier:@"c" forPath:collisionBound];
collisionBehavior.collisionMode = UICollisionBehaviorModeBoundaries;
return collisionBehavior;
}
- (UISnapBehavior *)snapBehaviorFromSnapView:(UIView *)view toPoint:(CGPoint)point {
if (!view) {
return nil;
}
UISnapBehavior *snapBehavior = [[UISnapBehavior alloc] initWithItem:view snapToPoint:point];
snapBehavior.damping = 0.75f;
return snapBehavior;
}
#pragma mark - UICollisionBehaviorDelegate
- (void)collisionBehavior:(UICollisionBehavior *)behavior endedContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier {
NSMutableSet *viewsToRemove = [[NSMutableSet alloc] init];
for (id aBehavior in _dynamicAnimator.behaviors) {
if ([aBehavior isKindOfClass:[UIAttachmentBehavior class]]) {
NSArray *items = ((UIAttachmentBehavior *)aBehavior).items;
if ([items containsObject:item]) {
[_dynamicAnimator removeBehavior:aBehavior];
[viewsToRemove addObjectsFromArray:items];
}
}
if ([aBehavior isKindOfClass:[UIPushBehavior class]]) {
NSArray *items = ((UIPushBehavior *)aBehavior).items;
if ([((UIPushBehavior *)aBehavior).items containsObject:item]) {
if ([items containsObject:item]) {
[_dynamicAnimator removeBehavior:aBehavior];
[viewsToRemove addObjectsFromArray:items];
}
}
}
if ([aBehavior isKindOfClass:[UICollisionBehavior class]]) {
NSArray *items = ((UICollisionBehavior *)aBehavior).items;
if ([((UICollisionBehavior *)aBehavior).items
containsObject:item]) {
if ([items containsObject:item]) {
[_dynamicAnimator removeBehavior:aBehavior];
[viewsToRemove addObjectsFromArray:items];
}
}
}
}
}
#pragma mark - dynamic
- (void)setDynamicWithView:(UIView *)view model:(DLCardFrameModel *)model duration:(NSTimeInterval)duration{
[UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
view.userInteractionEnabled = NO;
view.hidden = model.hidden;
CGAffineTransform t = CGAffineTransformMakeRotation(model.rotation);
view.transform = CGAffineTransformScale(t, model.scale, model.scale);
view.center = CGPointMake(model.x, model.y);
if (_attachmentView) {
[self insertSubview:view belowSubview:_attachmentView];
}
}completion:nil];
}
- (void)movieViewWithDistance:(CGFloat)distance view:(UIView *)view{
CABasicAnimation *postionAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"];
postionAnimation.duration = 5;
postionAnimation.fromValue = @(view.center.y);
postionAnimation.toValue = @(view.center.y - distance);
postionAnimation.removedOnCompletion = NO;
postionAnimation.delegate = self;
postionAnimation.autoreverses = NO;
postionAnimation.fillMode = kCAFillModeForwards;
postionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[view.layer addAnimation:postionAnimation forKey:@"posstionAnimation"];
}
@end
//
// DLCardFrameModel.h
// dielianw
//
// Created by 刘威 on 16/8/10.
// Copyright © 2016年 apple. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface DLCardFrameModel : NSObject
@property (assign, nonatomic, readonly) CGFloat rotation;
@property (assign, nonatomic ) CGFloat scale;
@property (assign, nonatomic, getter = getPoint) CGPoint point;
@property (assign, nonatomic, readonly) CGFloat x;
@property (assign, nonatomic ) CGFloat y;
@property (assign, nonatomic, readonly) BOOL hidden;
- (instancetype)initWithRotation:(CGFloat)rotation scale:(CGFloat)scale x:(CGFloat)x y:(CGFloat)y hidden:(BOOL)hidden;
@end
//
// DLCardFrameModel.m
// dielianw
//
// Created by 刘威 on 16/8/10.
// Copyright © 2016年 apple. All rights reserved.
//
#import "DLCardFrameModel.h"
@implementation DLCardFrameModel
- (instancetype)initWithRotation:(CGFloat)rotation scale:(CGFloat)scale x:(CGFloat)x y:(CGFloat)y hidden:(BOOL)hidden{
if (self = [super init]) {
_rotation = rotation;
_scale = scale;
_x = x;
_y = y;
_hidden = hidden;
}
return self;
}
- (CGPoint)getPoint {
_point = CGPointMake(_x, _y);
return _point;
}
@end