1.效果图如下:
1.拖拽时分三种状态:
- (void)panOfRemoveMode:(UIPanGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan) {
// 初始手指滑动的距离
__fingerPoiX = 0;
// 当前项
__curItem = self.visiableItems[0];
// 上一项
__lastItem = __visibleIndex > 0 ? [self itemAtIndex:__visibleIndex - 1] : nil;
if (__lastItem) {
// 先添加上并设置初始位置
[__lastItem removeAlphaMaskView];
[self addSubview:__lastItem];
[self setFinalFrameForItem:__lastItem atIndex:-1 isUpdate:NO isLeftFinal:YES];
[self setTransformForItem:__lastItem atIndex:0];
}
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
CGPoint movedPoint = [gesture translationInView:self];
__fingerPoiX += movedPoint.x;
// 避免一个手势左右滑动,即操作了 curItem,又操作了 lastItem
if (__fingerPoiX < 0 && __lastItem) {
[__lastItem removeFromSuperview];
__lastItem = nil;
}
// 左滑且未至底端
if (__fingerPoiX < 0 && __visibleIndex < [self numberOfItems] - 1) {
[self calculateItemCenter:__curItem point:movedPoint isDeleteMode:NO];
[self adjustTranslateAngle:__curItem centerX:__curItem.center.x];
}
// 右滑且未至顶端
else if (__fingerPoiX > 0 && __visibleIndex > 0 && __lastItem) {
[self calculateItemCenter:__lastItem point:movedPoint isDeleteMode:NO];
[self adjustTranslateAngle:__lastItem centerX:__lastItem.center.x];
}
[gesture setTranslation:CGPointZero inView:self];
}
else if (gesture.state == UIGestureRecognizerStateEnded) {
CGPoint vel = [gesture velocityInView:self];
if (__fingerPoiX < 0 && __visibleIndex < [self numberOfItems] - 1) { // 左滑
if (vel.x < -800) {
[self cardItemOutOfScreenEndScrollAnimation:__curItem isLeftFinal:YES isFast:YES];
}
else if(__fingerPoiX < -100){
[self cardItemOutOfScreenEndScrollAnimation:__curItem isLeftFinal:YES isFast:NO];
}
else {
[self cardItemToOriginalEndScrollAnimation:__curItem isFast:NO];
}
}
else if (__lastItem && __fingerPoiX > 0){ // 右滑
if(vel.x > 800) {
[self cardItemToOriginalEndScrollAnimation:__lastItem isFast:YES];
}
else if (__fingerPoiX > CARDITEM_RIGHT_RESPONDLENGTH){
[self cardItemToOriginalEndScrollAnimation:__lastItem isFast:NO];
}
else {
[self cardItemOutOfScreenEndScrollAnimation:__lastItem isLeftFinal:YES isFast:NO];
}
}
else {
// 只对当前可视视图进行纠错
[self cardItemToOriginalEndScrollAnimation:__curItem isFast:NO];
}
}
}
/**
* @brief 设置卡片项的放射变换
*/
- (void)setTransformForItem:(CardViewItem *)item atIndex:(NSInteger)idx
{
CGAffineTransform scale = CGAffineTransformMakeScale(1 - self.scaleRatio * idx, 1);
item.transform = CGAffineTransformTranslate(scale, 0, 15 * idx);
}
/**
* @brief 设置卡片项的最终位置约束
* @param idx 索引,用于新添加 item 还未设置 frame 时添加约束
* @param isUpdate 是否是更新约束
* @param isLeft yes - 最终位置在左侧 no - 最终位置在右侧
*/
- (void)setFinalFrameForItem:(CardViewItem *)item atIndex:(NSInteger)idx isUpdate:(BOOL)isUpdate isLeftFinal:(BOOL)isLeft
{
// cx 代表 item.center.x; centerX 代表 item 与 self 中心点的距离
NSInteger cx = -300;
NSInteger centerX = cx - self.center.x;
// 如图:|← 300 →□← 300 →|( | 代表 item.center.x 位置, 300 代表距离,□ 代表self 视图)
if (!isLeft) {
cx = -cx + W(self); centerX = -centerX;
}
if (isUpdate) {
// 设置旋转角度
[self adjustTranslateAngle:item centerX:cx];
item.center = CGPointMake(cx, H(self)/2 + 100);
[item mas_updateConstraints:^(MASConstraintMaker * make) {
make.centerX.equalTo(@(centerX));
make.centerY.equalTo(@(100));
}];
}
else {
CGRect rect = [self itemRectAtIndex:idx];
item.center = CGPointMake(cx, H(self)/2 + 100);
[item mas_makeConstraints:^(MASConstraintMaker * make) {
make.centerX.equalTo(@(centerX));
make.centerY.equalTo(@(100));
make.width.equalTo(@(rect.size.width));
make.height.equalTo(@(rect.size.height));
}];
}
}
/**
* @brief 设置卡片项的初始位置约束
* @attention item 缩放时其子视图也缩小;还原时,如果 item 与父视图没有约束,则 item 的子视图不会还原且在滑动时界面出错
*/
- (void)setOriginalFrameForItem:(CardViewItem *)item atIndex:(NSInteger)idx isUpdate:(BOOL)isUpdate
{
SELF_WEAK;
if (isUpdate) {
// 约束不会导致 frame 调整
item.center = CGPointMake(W(self)/2, H(item)/2);
item.transform = CGAffineTransformMakeRotation(0);
[item mas_updateConstraints:^(MASConstraintMaker * make) {
SELF_STRONG;
make.centerX.equalTo(strongSelf);
make.centerY.equalTo(@((H(item) - H(strongSelf))/2 ));
}];
}
else {
CGRect rect = [self itemRectAtIndex:idx];
item.center = CGPointMake(W(self) / 2, rect.size.height / 2);
[item mas_makeConstraints:^(MASConstraintMaker * make) {
SELF_STRONG;
make.centerX.equalTo(strongSelf);
make.centerY.equalTo(@((rect.size.height - H(strongSelf)) /2 ));
make.width.equalTo(@(rect.size.width));
make.height.equalTo(@(rect.size.height));
}];
}
}
/**
* @brief 从复用项数组中获取可复用对象,没有则新创建 。仿tableview的cell重用机制
*/
- (CardViewItem *)dequeueReusableCellWithIdentifier:(NSString *)identifier
{
__block CardViewItem * item = nil;
// 有可复用项 + 剩余的项 > _maxItems
if (self.reusableItems.count > 0 && __visibleIndex <= [self numberOfItems] - self.maxItems) {
[self.reusableItems enumerateObjectsUsingBlock:^(CardViewItem * obj, NSUInteger idx, BOOL * stop) {
if ([obj.reuseIdentifier isEqualToString:identifier]) {
item = obj;
*stop = YES;
}
}];
}
else {
[self.mapDict enumerateKeysAndObjectsUsingBlock:^(NSString * key, id obj, BOOL * stop) {
if ([key isEqualToString:identifier]) {
if ([obj isKindOfClass:[NSString class]]) { // xib 文件
item = (CardViewItem *)[self viewFromXibFile:(NSString *)obj];
}
else { // 类文件
item = [[(Class)obj alloc] init];
}
item.reuseIdentifier = key;
[item addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(tap:)]];
*stop = YES;
}
}];
}
return item;
}