iOS開發系列之常用自訂控制項開發集—自訂UITableViewCell側滑菜單控制項開發
在很多app中就有UITableViewCell左滑出現菜單如系統刪除按鈕,但是系統的只能有一個,有很多需求需要個人化不僅可以放文字還可以放按鈕修改背景色建立多個功能表項目,那麼系統提供的肯定不適合,所以我們需要自己手工打造。
直接上代碼如下:
WHC_MenuCell.h 標頭檔如下:
//// WHC_MenuCell.m// WHC_MenuCell//// Created by 吳海超 on 15/4/3.// Copyright (c) 2015年 Sinosun Technology Co., Ltd. All rights reserved.//#import @class WHC_MenuCell;@protocol WHC_MenuCellDelegate - (BOOL)WHC_MenuCell:(WHC_MenuCell*)whcCell didPullCell:(NSInteger)index; //拉動tableView的回調@end@interface WHC_MenuCell : UITableViewCell@property (nonatomic,assign) CGFloat menuViewWidth; //菜單總寬度@property (nonatomic,retain) NSArray * menuItemTitles; //每個菜單的標題@property (nonatomic,retain) NSArray * menuItemTitleColors; //每個菜單的文字顏色@property (nonatomic,retain) NSArray * menuItemBackImages; //每個菜單的背景圖片@property (nonatomic,retain) NSArray * menuItemNormalImages; //每個菜單正常的圖片@property (nonatomic,retain) NSArray * menuItemSelectedImages; //每個菜單選中的圖片@property (nonatomic,retain) NSArray * menuItemBackColors; //每個菜單的背景顏色@property (nonatomic,retain) NSArray * menuItemWidths; //每個菜單的寬度@property (nonatomic,strong) UIView * ContentView; //自訂內容view@property (nonatomic,assign) CGFloat fontSize; //字型大小@property (nonatomic,assign) NSInteger index; //cell下標@property (nonatomic,assign) iddelegate; //cell代理//單擊功能表項目- (void)clickMenuItem:(UIButton*)sender;//關閉菜單- (BOOL)closeCellWithAnimation:(BOOL)b;//關閉批量菜單- (BOOL)closeCellWithTableView:(UITableView*)tableView index:(NSInteger)index animation:(BOOL)b;//開始或者正在拉開菜單- (void)startScrollviewCell:(BOOL)state x:(CGFloat)moveX;//結束拉開菜單- (void)didEndScrollViewCell:(BOOL)state;@end
WHC_MenuCell.m源檔案如下:
//// WHC_MenuCell.m// WHC_MenuCell//// Created by 吳海超 on 15/4/3.// Copyright (c) 2015年 Sinosun Technology Co., Ltd. All rights reserved.//#import "WHC_MenuCell.h"#define KWHC_MENUCELL_ANMATION_PADING (10.0)@interface WHC_MenuCell (){ BOOL _isOpen; //是否開啟菜單 BOOL _isScorllClose; //是否滾動關閉菜單 CGFloat _startX; //儲存拉開菜單開始觸摸x座標 UIView * _menuView; //菜單view UIPanGestureRecognizer * _panGesture; //手勢}@end@implementation WHC_MenuCell//初始化UI- (void)initUI{ _panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)]; _panGesture.delegate = self; [self.contentView addGestureRecognizer:_panGesture]; UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTapGestrue:)]; tapGesture.delegate = self; [self.contentView addGestureRecognizer:tapGesture]; if(_menuItemTitles == nil){ _menuItemTitles = @[]; } if(_menuItemBackImages == nil){ _menuItemBackImages = @[]; } if(_menuItemBackColors == nil){ _menuItemBackColors = @[[UIColor redColor]]; } if(_menuItemTitleColors == nil){ _menuItemTitleColors = @[[UIColor blackColor]]; } if(_menuItemWidths == nil){ _menuItemWidths = @[]; } if(_menuItemNormalImages == nil){ _menuItemNormalImages = @[]; } if(_menuItemSelectedImages == nil){ _menuItemSelectedImages = @[]; } CGFloat _menuViewX = CGRectGetWidth(_ContentView.frame) - _menuViewWidth; _menuView = [[UIView alloc]initWithFrame:CGRectMake(_menuViewX + CGRectGetMinX(_ContentView.frame), 0.0, _menuViewWidth, CGRectGetHeight(_ContentView.frame))]; _menuView.backgroundColor = [UIColor clearColor]; [self.contentView insertSubview:_menuView belowSubview:_ContentView]; NSInteger menuItemCount = _menuItemTitles.count; NSInteger menuBackImageCount = _menuItemBackImages.count; NSInteger menuBackColorCount = _menuItemBackColors.count; NSInteger menuTitleColorCount = _menuItemTitleColors.count; NSInteger menuItemWidthCount = _menuItemWidths.count; NSInteger menuItemNormalImageCount = _menuItemNormalImages.count; NSInteger menuItemSelectedImageCount = _menuItemSelectedImages.count; CGFloat btnWidth = _menuViewWidth / (CGFloat)menuItemCount; CGFloat (^currentWidth)(NSInteger i) = ^(NSInteger i){ CGFloat width = 0.0; for (NSInteger j = 0; j <= i ; j++) { width += [_menuItemWidths[j] floatValue]; } return width; }; //建立功能表按鈕 for (NSInteger i = 0; i < menuItemCount; i++) { UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.tag = i; CGRect btnRc = CGRectMake(i * btnWidth, 0.0, btnWidth, CGRectGetHeight(_ContentView.frame)); btn.frame = btnRc; if(menuItemWidthCount == menuItemCount){ btnRc.origin.x = currentWidth(i - 1); btnRc.size.width = [_menuItemWidths[i] floatValue]; btn.frame = btnRc; } [btn setTitle:_menuItemTitles[i] forState:UIControlStateNormal]; NSInteger titleColorIndex = i; if(titleColorIndex >= menuTitleColorCount){ titleColorIndex = menuTitleColorCount - 1; if(titleColorIndex < 0){ titleColorIndex = 0; } } if(titleColorIndex < menuTitleColorCount){ [btn setTitleColor:_menuItemTitleColors[titleColorIndex] forState:UIControlStateNormal]; } NSInteger imageIndex = i; if(imageIndex >= menuBackImageCount){ imageIndex = menuBackImageCount - 1; if(imageIndex < 0){ imageIndex = 0; } } if(imageIndex > menuBackImageCount){ [btn setBackgroundImage:[UIImage imageNamed:_menuItemBackImages[imageIndex]] forState:UIControlStateNormal]; } NSInteger colorIndex = i; if(colorIndex >= menuBackColorCount){ colorIndex = menuBackColorCount - 1; if(colorIndex < 0){ colorIndex = 0; } } if(colorIndex < menuBackColorCount){ btn.backgroundColor = _menuItemBackColors[colorIndex]; } if(i < menuItemNormalImageCount){ NSString * imageName = _menuItemNormalImages[i]; if(imageName != nil && imageName.length){ [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal]; } if(i < menuItemSelectedImageCount){ NSString * selectedImageName = _menuItemSelectedImages[i]; if(selectedImageName != nil && selectedImageName.length){ [btn setImage:[UIImage imageNamed:selectedImageName] forState:UIControlStateHighlighted]; } } } btn.titleLabel.minimumScaleFactor = 0.1; btn.titleLabel.adjustsFontSizeToFitWidth = YES; if(_fontSize == 0.0){ _fontSize = 18.0; } btn.titleLabel.font = [UIFont boldSystemFontOfSize:_fontSize]; [btn addTarget:self action:@selector(clickMenuItem:) forControlEvents:UIControlEventTouchUpInside]; [_menuView addSubview:btn]; }}//載入xib- (void)awakeFromNib{ [self initUI];}- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if(self){ } return self;}//設定滾動列表時菜單關閉狀態- (void)setIsScrollClose{ _isScorllClose = NO;}//下面兩個方法有子類實現屬於觸摸監聽方法- (void)startScrollviewCell:(BOOL)state x:(CGFloat)moveX{}- (void)didEndScrollViewCell:(BOOL)state{}//單擊功能表項目- (void)clickMenuItem:(UIButton *)sender{ [self closeCellWithAnimation:YES];}//批量關閉tableview上得多個cell菜單- (BOOL)closeCellWithTableView:(UITableView*)tableView index:(NSInteger)index animation:(BOOL)b{ NSArray * indexPathArr = [tableView indexPathsForVisibleRows]; BOOL handleResult = NO; for (NSIndexPath * indexPath in indexPathArr) { if(_index != indexPath.row && index > -1){ WHC_MenuCell * cell = (WHC_MenuCell *)[tableView cellForRowAtIndexPath:indexPath]; [cell setIsScrollClose]; if([cell closeCellWithAnimation:b]){ handleResult = YES; } }else if(index <= -1){ WHC_MenuCell * cell = (WHC_MenuCell *)[tableView cellForRowAtIndexPath:indexPath]; if(_index != indexPath.row){ [cell setIsScrollClose]; } if([cell closeCellWithAnimation:b]){ handleResult = YES; } } } return handleResult;}//關閉cell菜單- (BOOL)closeCellWithAnimation:(BOOL)b{ BOOL isClose = NO; if(_isOpen){ isClose = YES; if(b){ [UIView animateWithDuration:0.2 animations:^{ _ContentView.transform = CGAffineTransformMakeTranslation(0.0, 0.0); }completion:^(BOOL finished) { _isOpen = NO; [self didEndScrollViewCell:_isOpen]; }]; }else{ _ContentView.transform = CGAffineTransformMakeTranslation(0.0, 0.0); _isOpen = NO; [self didEndScrollViewCell:_isOpen]; } } return isClose;}- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state}//手勢處理- (void)handlePanGesture:(UIPanGestureRecognizer*)panGesure{ switch (panGesure.state) { case UIGestureRecognizerStateBegan:{ _startX = _ContentView.transform.tx; _isScorllClose = [_delegate WHC_MenuCell:self didPullCell:_index]; } break; case UIGestureRecognizerStateChanged:{ if(_isScorllClose && _isOpen == NO){ return; } CGFloat currentX = _ContentView.transform.tx; CGFloat moveDistanceX = [panGesure translationInView:panGesure.view].x; CGFloat velocityX = [panGesure velocityInView:panGesure.view].x; CGFloat moveX = _startX + moveDistanceX; if(velocityX > 0){//right if(currentX >= KWHC_MENUCELL_ANMATION_PADING){ [panGesure setTranslation:CGPointMake(KWHC_MENUCELL_ANMATION_PADING, 0.0) inView:panGesure.view]; break; } }else{ if(currentX < -_menuViewWidth){ moveX = currentX - 0.4; [panGesure setTranslation:CGPointMake(moveX, 0.0) inView:panGesure.view]; } } _ContentView.transform = CGAffineTransformMakeTranslation(moveX, 0.0); [self startScrollviewCell:_isOpen x:moveDistanceX]; } break; case UIGestureRecognizerStateCancelled: case UIGestureRecognizerStateEnded:{ _isScorllClose = NO; if(_ContentView.transform.tx > 0.0){ [UIView animateWithDuration:0.2 animations:^{ _ContentView.transform = CGAffineTransformMakeTranslation(-KWHC_MENUCELL_ANMATION_PADING, 0.0); }completion:^(BOOL finished) { _isOpen = NO; [self didEndScrollViewCell:_isOpen]; [UIView animateWithDuration:0.2 animations:^{ _ContentView.transform = CGAffineTransformMakeTranslation(0.0, 0.0); }]; }]; }else if (_ContentView.transform.tx < 0){ CGFloat tx = fabsf(_ContentView.transform.tx); if(tx < _menuViewWidth / 2.0 || (tx < _menuViewWidth && _isOpen)){ [UIView animateWithDuration:0.2 animations:^{ _ContentView.transform = CGAffineTransformMakeTranslation(0.0, 0.0); }completion:^(BOOL finished) { _isOpen = NO; [self didEndScrollViewCell:_isOpen]; }]; }else{ [UIView animateWithDuration:0.2 animations:^{ _ContentView.transform = CGAffineTransformMakeTranslation(-_menuViewWidth, 0.0); }completion:^(BOOL finished) { _isOpen = YES; [self didEndScrollViewCell:_isOpen]; }]; } } } break; default: break; }}- (void)handleTapGestrue:(UITapGestureRecognizer*)tapGesture{}#pragma mark - UIGestureRecognizerDelegate- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ if([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){ return [_delegate WHC_MenuCell:self didPullCell:-1]; }else if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class] ]){ UIPanGestureRecognizer * panGesture = (UIPanGestureRecognizer*)gestureRecognizer; CGPoint velocityPoint = [panGesture velocityInView:panGesture.view]; if(fabsf(velocityPoint.x) > fabsf(velocityPoint.y)){ return YES; }else{ _isScorllClose = [_delegate WHC_MenuCell:self didPullCell:-1]; return _isScorllClose; } } return NO;}@end