iOS 自定義的卡片流互動控制元件

chenzhengxu發表於2018-03-03

CardView

模仿探探的卡片流互動控制元件,專案地址:(github.com/chenzhengxu…)

image

如何使用CardView

  • 通過CocoaPods安裝:pod 'CardView'
  • 手動匯入:
    • 把CardView資料夾內的所有檔案拖入工程
    • 匯入主要檔案:#import "CardView"
CardView.h                  CardItemView.h            
複製程式碼

控制元件介紹

控制元件模仿了tableview的資料來源和代理方法,以及tableviewcell的快取池邏輯

#import <UIKit/UIKit.h>
#import "CardItemView.h"

@class CardView;

@protocol CardViewDelegate <NSObject>

@optional
/**  點選了第幾個itemView*/
- (void)cardView:(CardView *)cardView didClickItemAtIndex:(NSInteger)index;

@end

@protocol CardViewDataSource <NSObject>

@required
/**  一共有多少個CardItemView物件*/
- (NSInteger)numberOfItemViewsInCardView:(CardView *)cardView;
/**  返回第幾個CardItemView的物件*/
- (CardItemView *)cardView:(CardView *)cardView itemViewAtIndex:(NSInteger)index;
/**  要求請求更多資料*/
- (void)cardViewNeedMoreData:(CardView *)cardView;

@optional
- (CGSize)cardView:(CardView *)cardView sizeForItemViewAtIndex:(NSInteger)index;

@end

@interface CardView : UIView

/**  資料來源*/
@property (nonatomic, weak) id <CardViewDataSource> dataSource;
/**  代理*/
@property (nonatomic, weak) id <CardViewDelegate> delegate;

/**  獲取識別符號的CardItemView物件*/
- (CardItemView *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
/**  刪除第一個CardItemView物件 是否從左側*/
- (void)deleteTheTopItemViewWithLeft:(BOOL)left;
/**  過載檢視*/
- (void)reloadData;

@end

複製程式碼
#import <UIKit/UIKit.h>

@class CardItemView;

@protocol CardItemViewDelegate <NSObject>
/**  itemView從父檢視移除*/
- (void)cardItemViewDidRemoveFromSuperView:(CardItemView *)CardItemView;
/**  item移動了多少角度,是否有動畫*/
- (void)cardItemViewDidMoveRate:(CGFloat)rate anmate:(BOOL)anmate;

@end

@interface CardItemView : UIView

/**  代理*/
@property (weak, nonatomic) id<CardItemViewDelegate> delegate;
/**  識別符號*/
@property (nonatomic, readonly, copy) NSString *reuseIdentifier;

/**  初始化檢視*/
- (void)initView;
/**  初始化檢視,繫結識別符號*/
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
/**  從父檢視移除,是否從左側*/
- (void)removeWithLeft:(BOOL)left;

@end
複製程式碼

自定義

繼承於CardItemView即可進行自定義的卡片流定製

快取池

為了優化記憶體,建立了快取池,預設同時顯示4個CardItemView物件。

@property (copy, nonatomic) NSMutableDictionary *reuseDict;
複製程式碼

同時最多隻有螢幕上可見的CardItemView物件存在記憶體中,可允許多個不同reuseIdentifier的CardItemView物件進行快取呼叫,當第一個CardItemView物件從檢視上移除時,依然會儲存在快取池字典中,同時呼叫

- (CardItemView *)cardView:(CardView *)cardView itemViewAtIndex:(NSInteger)index;
複製程式碼

去獲取最後個CardItemView物件,加入在檢視顯示中。

預設從最後提前5張呼叫datasource方法,類似於tableview的上拉載入

- (void)cardViewNeedMoreData:(CardView *)cardView;
複製程式碼

動畫

動畫原理,在CardItemView上新增了UIPanGestureRecognizer,根據拖拽手勢的action方法@selector(panGestHandle:)反饋的拖拽情況,來判斷CardItemView物件應該旋轉多少角度,是否應該從父檢視移除。

- (void)panGestHandle:(UIPanGestureRecognizer *)panGest {
    if (panGest.state == UIGestureRecognizerStateChanged) {
        CGPoint movePoint = [panGest translationInView:self];
        _isLeft = (movePoint.x < 0);
        self.center = CGPointMake(self.center.x + movePoint.x, self.center.y + movePoint.y);
        
        CGFloat angle = (self.center.x - self.frame.size.width / 2.0) / self.frame.size.width / 4.0;
        _currentAngle = angle;
        self.transform = CGAffineTransformMakeRotation(-angle);
        
        [panGest setTranslation:CGPointZero inView:self];
        if ([self.delegate respondsToSelector:@selector(cardItemViewDidMoveRate:anmate:)]) {
            CGFloat rate = fabs(angle)/0.15>1 ? 1 : fabs(angle)/0.15;
            [self.delegate cardItemViewDidMoveRate:rate anmate:NO];
        }
        
    } else if (panGest.state == UIGestureRecognizerStateEnded) {
        CGPoint vel = [panGest velocityInView:self];
        if (vel.x > 800 || vel.x < - 800) {
            [self remove];
            return ;
        }
        if (self.frame.origin.x + self.frame.size.width > 150 && self.frame.origin.x < self.frame.size.width - 150) {
            [UIView animateWithDuration:0.5 animations:^{
                self.center = _originalCenter;
                self.transform = CGAffineTransformMakeRotation(0);
                if ([self.delegate respondsToSelector:@selector(cardItemViewDidMoveRate:anmate:)]) {
                    [self.delegate cardItemViewDidMoveRate:0 anmate:YES];
                }
            }];
        } else {
            [self remove];
        }
    }
}
複製程式碼

根據CardItemView物件的下標index來調整每個卡片不同的frame來體現卡片的層疊感。

CGSize size = [self itemViewSizeAtIndex:index];
[self insertSubview:itemView atIndex:0];
itemView.tag = index+1;
itemView.frame = CGRectMake(self.frame.size.width / 2.0 - size.width / 2.0, self.frame.size.height / 2.0 - size.height / 2.0, size.width, size.height);
複製程式碼

相關文章