美文网首页QiShare文章汇总iOS界面特效篇
iOS 自定义拖拽式控件:QiDragView

iOS 自定义拖拽式控件:QiDragView

作者: QiShare | 来源:发表于2019-02-14 17:13 被阅读98次

    级别: ★★☆☆☆
    标签:「iOS」「拖拽式控件」「QiDragView」
    作者: MrLiuQ
    审校: QiShare团队


    首先,我们先看一下QiDragView的效果图:

    一、QiDragView整体架构设计

    话不多说,上架构图~

    QiDragView(QiDragSortView的简称)是一种可选择可拖拽的自定义控件,可以满足一些拖拽排序的业务需求场景。

    二、如何自定义使用QiDragView?

    在上Demo之前,先介绍几个可以自定义的UI配置属性:

    属性 类型 介绍
    rowHeight CGFloat 行高
    rowMargin CGFloat 行边距
    rowPadding CGFloat 行间距
    columnMargin CGFloat 列边距
    columnPadding CGFloat 列间距
    columnCount NSInteger 列数
    normalColor UIColor 按钮基本字体颜色
    selectedColor UIColor 按钮选择字体颜色

    以及一些逻辑配置属性:

    属性 类型 介绍
    enabledTitles NSArray<NSString *> 可以被点击的titles(上行参数,默认全选)
    selectedTitles NSArray<NSString *> 可以被选择的titles(上行参数,默认全选)
    titles NSArray<NSString *> 按钮的titles(上行参数,会根据titles,创建出对应的button)

    使用起来也很方便:

    • 直接设置titles即可创建出对应title的Buttons。
    • 通过dragSortEnded的block方法回调,处理拖拽后的业务逻辑:按钮的排序、按钮是否选择等属性

    默认配置用法:

    QiDragSortView *dragSortView = [[QiDragSortView alloc] initWithFrame:CGRectMake(.0, 100.0, self.view.bounds.size.width, .0)];
    dragSortView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:.5];
    
    dragSortView.titles = @[@"首页推荐", @"奇舞周刊", @"众成翻译", @"QiShare", @"HULK一线杂谈", @"Qtest之道"];//!< 初始的Buttons(必填)
    [self.view addSubview:dragSortView];
    
    //! 拖拽方法回调:能拿到Button数组的排序和选择状态
    dragSortView.dragSortEnded = ^(NSArray<UIButton *> * _Nonnull buttons) {
        for (UIButton *button in buttons) {
            NSLog(@"title: %@, selected: %i", button.currentTitle, button.isSelected);
        }
    };
    

    自定义配置用法:

    QiDragSortView *dragSortView = [[QiDragSortView alloc] initWithFrame:CGRectMake(.0, 100.0, self.view.bounds.size.width, .0)];
    dragSortView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:.5];
    dragSortView.rowHeight = 50.0;
    dragSortView.rowMargin = 30.0;
    dragSortView.rowPadding = 20.0;
    dragSortView.columnCount = 3;
    dragSortView.columnMargin = 30.0;
    dragSortView.columnPadding = 20.0;
    dragSortView.normalColor = [UIColor redColor];
    dragSortView.selectedColor = [UIColor purpleColor];
    dragSortView.enabledTitles = @[@"奇舞周刊", @"众成翻译", @"QiShare", @"HULK一线杂谈", @"Qtest之道"];//!< 可以点击选择的Buttons(选填,默认全选)
    dragSortView.selectedTitles = @[@"首页推荐", @"HULK一线杂谈", @"Qtest之道"];//!< 初始选择的Buttons(选填,默认全选)
    dragSortView.titles = @[@"首页推荐", @"奇舞周刊", @"众成翻译", @"QiShare", @"HULK一线杂谈", @"Qtest之道"];//!< 初始的Buttons(必填)
    [self.view addSubview:dragSortView];
    
    //! 拖拽方法回调:能拿到Button数组的排序和选择状态
    dragSortView.dragSortEnded = ^(NSArray<UIButton *> * _Nonnull buttons) {
        for (UIButton *button in buttons) {
            NSLog(@"title: %@, selected: %i", button.currentTitle, button.isSelected);
        }
    };
    

    三、QiDragView的技术点

    3.1 长按手势:

    长按手势分别对应三种状态:UIGestureRecognizerStateBeganUIGestureRecognizerStateChangedUIGestureRecognizerStateEnded

    状态 说明
    UIGestureRecognizerStateBegan 长按手势开始
    UIGestureRecognizerStateChanged 长按手势拖拽中(进行时)
    UIGestureRecognizerStateEnded 长按手势结束
    //! 长按手势
    - (void)longPress:(UILongPressGestureRecognizer *)gesture {
        
        UIButton *currentButton = (UIButton *)gesture.view;
        
        if (gesture.state == UIGestureRecognizerStateBegan) {
            
            [self bringSubviewToFront:currentButton];
            
            [UIView animateWithDuration:.25 animations:^{
                self.originButtonCenter = currentButton.center;
                self.originGesturePoint = [gesture locationInView:currentButton];
                currentButton.transform = CGAffineTransformScale(currentButton.transform, 1.2, 1.2);
            }];
        }
        else if (gesture.state == UIGestureRecognizerStateEnded) {
            
            [UIView animateWithDuration:.25 animations:^{
                currentButton.center = self.originButtonCenter;
                currentButton.transform = CGAffineTransformIdentity;
            } completion:^(BOOL finished) {
                if (self.dragSortEnded) {
                    self.dragSortEnded(self.buttons);
                }
            }];
        }
        else if (gesture.state == UIGestureRecognizerStateChanged) {
            
            CGPoint gesturePoint = [gesture locationInView:currentButton];
            CGFloat deltaX = gesturePoint.x - _originGesturePoint.x;
            CGFloat deltaY = gesturePoint.y - _originGesturePoint.y;
            currentButton.center = CGPointMake(currentButton.center.x + deltaX, currentButton.center.y + deltaY);
            
            NSInteger fromIndex = currentButton.tag;
            NSInteger toIndex = [self toIndexWithCurrentButton:currentButton];
            
            if (toIndex >= 0) {
                currentButton.tag = toIndex;
                
                if (toIndex > fromIndex) {
                    for (NSInteger i = fromIndex; i < toIndex; i++) {
                        UIButton *nextButton = _buttons[i + 1];
                        CGPoint tempPoint = nextButton.center;
                        [UIView animateWithDuration:.5 animations:^{
                            nextButton.center = self.originButtonCenter;
                        }];
                        _originButtonCenter = tempPoint;
                        nextButton.tag = i;
                    }
                }
                else if (toIndex < fromIndex) {
                    for (NSInteger i = fromIndex; i > toIndex; i--) {
                        UIButton *previousButton = self.buttons[i - 1];
                        CGPoint tempPoint = previousButton.center;
                        [UIView animateWithDuration:.5 animations:^{
                            previousButton.center = self.originButtonCenter;
                        }];
                        _originButtonCenter = tempPoint;
                        previousButton.tag = i;
                    }
                }
                [_buttons sortUsingComparator:^NSComparisonResult(UIButton *obj1, UIButton *obj2) {
                    return obj1.tag > obj2.tag;
                }];
            }
        }
    }
    

    3.2 配置按钮:

    设计思路:在属性titles的setter方法中,初始化并配置好各个Buttons。

    - (void)setTitles:(NSArray<NSString *> *)titles {
        
        _titles = titles;
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSInteger differCount = titles.count - self.buttons.count;
            
            if (differCount > 0) {
                for (NSInteger i = self.buttons.count; i < titles.count; i++) {
                    [self.buttons addObject:[self buttonWithTag:i]];
                }
            }
            else if (differCount < 0) {
                NSArray *extraButtons = [self.buttons subarrayWithRange:(NSRange){titles.count, self.buttons.count - titles.count}];
                [self.buttons removeObjectsInArray:extraButtons];
                for (UIButton *button in extraButtons) {
                    [button removeFromSuperview];
                }
            }
            
            self.enabledTitles = self.enabledTitles ?: titles;//!< 如果有,就传入,否则传入titles
            self.selectedTitles = self.selectedTitles ?: titles;
            
            for (NSInteger i = 0; i < self.buttons.count; i++) {
                [self.buttons[i] setTitle:titles[i] forState:UIControlStateNormal];
                [self.buttons[i] addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)]];//!< 长按手势
                [self selectButton:self.buttons[i] forStatus:[self.selectedTitles containsObject:titles[i]]];
                if ([self.enabledTitles containsObject:titles[i]]) {
                    [self.buttons[i] addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
                }
            }
            
            for (NSInteger i = 0; i < self.buttons.count; i++) {
                NSInteger rowIndex = i / self.columnCount;
                NSInteger columnIndex = i % self.columnCount;
                CGFloat buttonWidth = (self.bounds.size.width - self.columnMargin * 2 - self.columnPadding * (self.columnCount - 1))  / self.columnCount;
                CGFloat buttonX = self.columnMargin + columnIndex * (buttonWidth + self.columnPadding);
                CGFloat buttonY = self.rowMargin + rowIndex * (self.rowHeight + self.rowPadding);
                self.buttons[i].frame = CGRectMake(buttonX, buttonY, buttonWidth, self.rowHeight);
            }
            
            CGRect frame = self.frame;
            NSInteger rowCount = ceilf((CGFloat)self.buttons.count / (CGFloat)self.columnCount);
            frame.size.height = self.rowMargin * 2 + self.rowHeight * rowCount  + self.rowPadding * (rowCount - 1);
            self.frame = frame;
        });
    }
    

    源码地址:QiDragView


    小编微信:可加并拉入《QiShare技术交流群》。

    关注我们的途径有:
    QiShare(简书)
    QiShare(掘金)
    QiShare(知乎)
    QiShare(GitHub)
    QiShare(CocoaChina)
    QiShare(StackOverflow)
    QiShare(微信公众号)

    推荐文章:
    iOS Wireshark抓包
    iOS Charles抓包
    初探TCP
    IP、UDP初探
    奇舞周刊

    相关文章

      网友评论

        本文标题:iOS 自定义拖拽式控件:QiDragView

        本文链接:https://www.haomeiwen.com/subject/eyaeeqtx.html