項目中遇到過拖動按鈕排序的效果需求,網上的也只是寫零散的講解,簡單的研究過後做了個Demo並封裝了自訂控制項供大家一起學習.廢話不多講,先上效果圖再說:
PS:也請大家支援IM_Loser 還有我的GitHub :IMLoser如有不足 大家一起討論~
Demo地址 : https://github.com/IMLoser/HWSortViewDemo 記的點個星星支援下喲~
總結起來,其實想要實現這個效果有這麼幾個解決的步驟:
1. 九宮格的排序
2. 點擊按鈕拖動(包含添加手勢,監聽手勢的狀態變化...)
3. 長按按鈕觸發可拖動狀態(我這裡用的是長按按鈕變大和變透明...)
4. 記錄按鈕初始位置,拖動後可自動複位(就是用屬性記錄拖動前按鈕中心點的位置...)
5. 在手勢方法中判斷拖動時是否進入其他按鈕範圍(如果重疊,開始根據tag排序按鈕位置,從而實現對應的UI效果...)
下面展示核心代碼吧
#import "HWSortView.h"#define SELF_SIZE self.frame.size#define DEFAULT_COLUMN_MARGIN 10#define DEFAULT_COLUMN_COUNT 3#define RGB_COLOR [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:0.8]@interface HWSortView ()@property (strong, nonatomic) NSMutableArray * buttons;@property (assign, nonatomic) CGPoint startP;@property (assign, nonatomic) CGPoint buttonP;@end@implementation HWSortView- (NSMutableArray *)buttons { if (!_buttons) { _buttons = [NSMutableArray array]; } return _buttons;}+ (instancetype)sortView{ HWSortView * sortView = [[HWSortView alloc] init]; sortView.backgroundColor = [UIColor clearColor]; return sortView;}+ (instancetype)sortViewWithTitlesArray:(NSMutableArray *)titlesArray{ HWSortView * sortView = [HWSortView sortView]; sortView.titlesArray = titlesArray; [sortView initailButtons]; return sortView;}- (void)initailButtons{ if (!_titlesArray.count) return; for (NSInteger i = 0; i < _titlesArray.count; i++) { UIButton * movedBtn = [UIButton buttonWithType:(UIButtonTypeSystem)]; [movedBtn setBackgroundColor:RGB_COLOR]; movedBtn.layer.cornerRadius = 5; [movedBtn setTitleColor:[UIColor whiteColor] forState:(UIControlStateNormal)]; [movedBtn setTitle:_titlesArray[i] forState:(UIControlStateNormal)]; [self.buttons addObject:movedBtn]; movedBtn.tag = i; [self addSubview:movedBtn]; // 添加長按手勢 UILongPressGestureRecognizer * longGes = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressClick:)]; [movedBtn addGestureRecognizer:longGes]; }}- (void)layoutSubviews{ [super layoutSubviews]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self settingButtonFrame]; });}// 布局九宮格- (void)settingButtonFrame{ if (0 == _columnMargin) { _columnMargin = DEFAULT_COLUMN_MARGIN; } if (0 == _columnCount) { _columnCount = DEFAULT_COLUMN_COUNT; } CGFloat buttonH = 30; CGFloat buttonW = (SELF_SIZE.width - _columnMargin * (_columnCount + 1)) / _columnCount * 1.0; CGFloat buttonX = 0; CGFloat buttonY = 0; // 設定按鈕初始位置 for (NSInteger i = 0; i < self.buttons.count; i++) { NSInteger column = i % _columnCount; NSInteger row = i / _columnCount; buttonX = _columnMargin + column * (buttonW + _columnMargin); buttonY = _columnMargin + row * (buttonH + _columnMargin); UIButton * btn = self.buttons[i]; btn.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH); }}// 設定按鈕文字顏色- (void)setTitlesColor:(UIColor *)titlesColor { _titlesColor = titlesColor; for (UIButton * movedBtn in _buttons) { [movedBtn setTitleColor:_titlesColor forState:(UIControlStateNormal)]; } }#pragma MARK - < Button_Gesture >- (void)longPressClick:(UIGestureRecognizer *)longGes{ UIButton * currentBtn = (UIButton *)longGes.view; if (UIGestureRecognizerStateBegan == longGes.state) { [UIView animateWithDuration:0.2 animations:^{ currentBtn.transform = CGAffineTransformScale(currentBtn.transform, 1.2, 1.2); currentBtn.alpha = 0.7f; _startP = [longGes locationInView:currentBtn]; _buttonP = currentBtn.center; }]; } if (UIGestureRecognizerStateChanged == longGes.state) { CGPoint newP = [longGes locationInView:currentBtn]; CGFloat movedX = newP.x - _startP.x; CGFloat movedY = newP.y - _startP.y; currentBtn.center = CGPointMake(currentBtn.center.x + movedX, currentBtn.center.y + movedY); // 擷取當前按鈕的索引 NSInteger fromIndex = currentBtn.tag; // 擷取目標移動索引 NSInteger toIndex = [self getMovedIndexByCurrentButton:currentBtn]; if (toIndex < 0) { return; } else { currentBtn.tag = toIndex; // 按鈕向後移動 if (fromIndex < toIndex) { for (NSInteger i = fromIndex; i < toIndex; i++) { // 拿到下一個按鈕 UIButton * nextBtn = self.buttons[i + 1]; CGPoint tempP = nextBtn.center; [UIView animateWithDuration:0.5 animations:^{ nextBtn.center = _buttonP; }]; _buttonP = tempP; nextBtn.tag = i; } [self sortArray]; } else if(fromIndex > toIndex) { // 按鈕向前移動 for (NSInteger i = fromIndex; i > toIndex; i--) { UIButton * previousBtn = self.buttons[i - 1]; CGPoint tempP = previousBtn.center; [UIView animateWithDuration:0.5 animations:^{ previousBtn.center = _buttonP; }]; _buttonP = tempP; previousBtn.tag = i; } [self sortArray]; } } } // 手指鬆開之後 進行的處理 if (UIGestureRecognizerStateEnded == longGes.state) { [UIView animateWithDuration:0.2 animations:^{ currentBtn.transform = CGAffineTransformIdentity; currentBtn.alpha = 1.0f; currentBtn.center = _buttonP; }]; }}- (void)sortArray{ // 對已改變按鈕的數組進行排序 [_buttons sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { UIButton *temp1 = (UIButton *)obj1; UIButton *temp2 = (UIButton *)obj2; return temp1.tag > temp2.tag; //將tag值大的按鈕向後移 }]; }// 擷取按鈕移動目標索引- (NSInteger)getMovedIndexByCurrentButton:(UIButton *)currentButton{ for (NSInteger i = 0; i<self.buttons.count ; i++) { UIButton * button = self.buttons[i]; if (!currentButton || button != currentButton) { if (CGRectContainsPoint(button.frame, currentButton.center)) { return i; } } } return -1;}@end