模仿UC新闻标签编辑功能

Posted 马大哈哈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模仿UC新闻标签编辑功能相关的知识,希望对你有一定的参考价值。

 

备注:博文只贴出关键代码,其中涉及到自定义控件、数据model可以忽略。博友使用过程中可以直接用button等替换掉报错的代码即可。

 

 ************************************ h 文件  ************************************

#import <UIKit/UIKit.h>

#import "DragButton.h"

 

/**

 *  非编辑状态,点击顶部标签回调到上一页数据

 *

 *  @param model 点击标签model

 */

typedef void(^DragButtonTapBlock) (DragButtonModel *model);

 

/**

 *  标签的增、减、坐标变换等,回调新数组到上一页

 *

 *  @param newArray 新数组

 */

typedef void(^DragButtonSortBlock) (NSMutableArray *newArray);

 

 

 

@interface DragButtonSortView : UIScrollView

<UIScrollViewDelegate,

UIGestureRecognizerDelegate> {

    

    NSMutableArray *_topDataArray ,*_bottomDataArray;     // 顶部 + 底部数据源

    NSMutableArray *_topButtonArray ,*_bottomButtonArray; // 顶部 + 底部存放button数组

    

    BOOL _dragDown;                                       // 向前(上)、向后(下)拖动

 

    CGFloat _criticalValue;                               // 临界值---顶部button区域的最后一个button底部Y坐标

    CGFloat _scrollY;                                     // scrollView 滚动的位置,支持自动滚动到顶部

  

    NSInteger _touchOldIndex;                             // 与哪个button交汇,button所在数组的索引

    

    CGPoint startPoint, originPoint;                      // 拖动相关变量

    CGPoint _dragStart,_dragCurrent;                      // 长按beginpoint + 持续拖动的point

    CGFloat minY, maxY;                                   // 是不是在水平方向拖动(最小Y + 最大Y

 

}

 

@property (nonatomic, copy) DragButtonTapBlock   tapBlock;   // 点击

@property (nonatomic, copy) DragButtonSortBlock  dragBlock;  // 拖动顺序变化

 

@property (nonatomic, strong) UILabel  *topLabel;

@property (nonatomic, strong) UIButton *editeButton;

@property (nonatomic, strong) UILabel  *bottomLabel;

@property (nonatomic, strong) UIButton *allButton;

 

 

/**

 *  初始化

 *

 *  @param frame          frame

 *  @param dragArray      可排序的数组

 *  @param normalArray    底部推荐区域的数组

 *  @param tBlock         可排序的自定义view正常状态下点击回调

 *  @param dBlock         可排序的数组的任何变动回调

 *

 *  @return 对象

 */

- (instancetype)initWithFrame:(CGRect)frame

                    dragArray:(NSArray *)dragArray

                  normalArray:(NSArray *)normalArray

                    tapButton:(DragButtonTapBlock)tBlock

              updateDragArray:(DragButtonSortBlock)dBlock;

 

@end

 

 ************************************ m 文件  ************************************

 

#import "DragButtonSortView.h"

 

static NSInteger const TopButtonTag    = 10000;

static NSInteger const BottomButtonTag = 20000;

static NSInteger const ButtonNumber    = 3; // 一行 3 个控件

 

@implementation DragButtonSortView

 

- (instancetype)initWithFrame:(CGRect)frame

                    dragArray:(NSArray *)dragArray

                  normalArray:(NSArray *)normalArray

                    tapButton:(DragButtonTapBlock)tBlock

              updateDragArray:(DragButtonSortBlock)dBlock {

    

    self = [super initWithFrame:frame];

    

    if (self) {

        

        self.backgroundColor = [UIColor whiteColor];

 

        self.delegate = self;

        

        _tapBlock   = tBlock;

        _dragBlock  = dBlock;

        _enterBlock = eBlock;

 

        _topDataArray      = [[NSMutableArray alloc] init];

        _bottomDataArray   = [[NSMutableArray alloc] init];

        _topButtonArray    = [[NSMutableArray alloc] init];

        _bottomButtonArray = [[NSMutableArray alloc] init];

                

        if (dragArray) {

            [_topDataArray addObjectsFromArray:dragArray];

        }

        if (normalArray) {

            [_bottomDataArray addObjectsFromArray:normalArray];

        }

 

        [self addSubview:self.topLabel];

        [self addSubview:self.editeButton];

        [self addSubview:self.bottomLabel];

        [self addSubview:self.allButton];

 

 

        [self loadTopDragButton:dragArray];

        [self loadBottomNormalButton:normalArray];

 

        self.contentOffset = CGPointMake(0, 0);

 

    }

    return self;

}

 

 

#pragma mark - 懒加载

 

- (UILabel *)topLabel {

 

    if (!_topLabel) {

        

        _topLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, SCREENWIDTH, 30)];

        _topLabel.backgroundColor = [UIColor clearColor];

        _topLabel.textColor = [UIColor grayColor];

        _topLabel.textAlignment = NSTextAlignmentLeft;

        _topLabel.font = [UIFont systemFontOfSize:15.0];

        _topLabel.text = @"进入";

 

    }

    

    return _topLabel;

}

 

- (UIButton *)editeButton {

 

    if (!_editeButton) {

   

        _editeButton = [UIButton buttonWithType:UIButtonTypeSystem];

        _editeButton.frame = CGRectMake(10, 0, SCREENWIDTH - 20, 30);

        _editeButton.backgroundColor = [UIColor clearColor];

        _editeButton.titleLabel.font = [UIFont systemFontOfSize:15.f];

        _editeButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

        [_editeButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];

        [_editeButton setTitle:@"编辑" forState:UIControlStateNormal];

        [_editeButton setTitle:@"完成" forState:UIControlStateSelected];

        [_editeButton addTarget:self action:@selector(editeButtonClick:) forControlEvents:UIControlEventTouchUpInside];

        

    }

    

    return _editeButton;

}

 

- (UILabel *)bottomLabel {

    

    if (!_bottomLabel) {

        

        _bottomLabel = [[UILabel alloc] initWithFrame:CGRectZero];

        _bottomLabel.backgroundColor = [UIColor clearColor];

        _bottomLabel.textColor = [UIColor grayColor];

        _bottomLabel.textAlignment = NSTextAlignmentLeft;

        _bottomLabel.font = [UIFont systemFontOfSize:15.0];

        _bottomLabel.text = @" ";

    }

    

    return _bottomLabel;

}

 

- (UIButton *)allButton {

    

    if (!_allButton) {

        

        _allButton = [UIButton buttonWithType:UIButtonTypeSystem];

        _allButton.frame = CGRectZero;

        _allButton.backgroundColor = [UIColor clearColor];

        _allButton.titleLabel.font = [UIFont systemFontOfSize:15.f];

        _allButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

        [_allButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];

        [_allButton setTitle:@"更多>" forState:UIControlStateNormal];

        [_allButton addTarget:self action:@selector(allButtonClick:) forControlEvents:UIControlEventTouchUpInside];

        

    }

    

    return _allButton;

}

 

- (void)loadTopDragButton:(NSArray *)array {

 

    CGFloat topY = CGRectGetMaxY(self.topLabel.frame);

    

    for (NSInteger i = 0; i < array.count; i++) {

       

        DragButtonModel *model = array[i];

 

        CGRect rect = [self getRectAtIndex:i topLocation:topY];

 

        DragButton *button = [[DragButton alloc] initWithFrame:rect deleteImageViewHiden:YES dataSource:model];

        button.tag = TopButtonTag + i;

        button.exclusiveTouch = YES;

        button.backgroundColor = RGBA(239, 239, 239, 1.0);

        button.titleLabel.font = [UIFont systemFontOfSize:15.f];

        [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

        [button setTitle:model.name forState:UIControlStateNormal];

        [button addTarget:self action:@selector(topButtonClick:) forControlEvents:UIControlEventTouchUpInside];

        [self addSubview:button];

        

        [self editeButtonGesture:button];

 

        [_topButtonArray addObject:button];

        

        if (i == array.count - 1) {

            _criticalValue = CGRectGetMaxY(button.frame) + 20;

        }

    }

    

    [self updateBottomLabelLocation];

}

 

- (void)loadBottomNormalButton:(NSArray *)array {

    

    CGFloat tempHeight = 0;

    CGFloat topY       = CGRectGetMaxY(self.bottomLabel.frame);

    

    for (NSInteger i = 0; i < array.count; i++) {

      

        DragButtonModel *model = array[i];

 

        CGRect rect = [self getRectAtIndex:i topLocation:topY];

 

        DragButton *button = [[DragButton alloc] initWithFrame:rect deleteImageViewHiden:YES dataSource:model];

        button.tag = BottomButtonTag + i;

        button.frame = rect;

        button.exclusiveTouch = YES;

        button.backgroundColor = RGBA(239, 239, 239, 1.0);

        button.titleLabel.font = [UIFont systemFontOfSize:15.f];

        [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

        [button setTitle:model.name forState:UIControlStateNormal];

        [button addTarget:self action:@selector(bottomButtonClick:) forControlEvents:UIControlEventTouchUpInside];

        [self addSubview:button];

     

        [_bottomButtonArray addObject:button];

 

        tempHeight = CGRectGetMaxY(button.frame) + 10;

    }

    

    CGFloat totalHeight = (tempHeight > SCREENHEIGHT) ? tempHeight : SCREENHEIGHT + 10;

    self.contentSize = CGSizeMake(self.frame.size.width, totalHeight);

}

 

 

#pragma mark - 基础功能

 

- (void)editeButtonClick:(UIButton *)button {

 

    self.editeButton.selected = !self.editeButton.selected;

    [self updateTopLabelTitle];

 

    BOOL hidenDeleteButton = (self.editeButton.selected) ? NO : YES;

    [self updateTopButtonDrag:hidenDeleteButton];

   

}

 

- (void)allButtonClick:(UIButton *)button {

 

    if (self.enterBlock) {

        self.enterBlock();

    }

}

 

/**

 *  顶部所有button的点击方法

 *  1.如果当前为编辑(拖动)状态,并且顶部所有button个数大于4(至少保留4个,反之提示),点击后从_topDataArray + _topButtonArray移除相应数据源 + 当前点击的button

      并把当前点击的button + 对应数据源插入_bottomButtonArray + _bottomDataArray

 *  2.数据处理完毕,执行移动到下面button区域动画。动画结束button移除之前绑定的事件,添加下部button的点击事件,移除绑定的长按事件,并回调新的顺序

 *  3.如果当前为正常状态(不能拖动),点击回调当前button对应的数据源

 *

 */

- (void)topButtonClick:(DragButton *)button {

    

    if (_editeButton.selected) { // 编辑状态,可以拖动,此时点击要移动到底部button区域

        if (_topButtonArray.count > 4) { // 至少保留 4

            

            NSInteger index = [_topButtonArray indexOfObject:button];

            BOOL lastOne = (index == _topButtonArray.count - 1) ? YES : NO;

           

            DragButtonModel *model = _topDataArray[index];

 

            [_topDataArray    removeObject:model];

            [_bottomDataArray insertObject:model atIndex:0];

            

            [_topButtonArray    removeObject:button];

            [_bottomButtonArray insertObject:button atIndex:0];

            

            [UIView animateWithDuration:0.3 animations:^{

                

                [self removeFromTopIsLastOne:lastOne currentIndex:index];

                

            } completion:^(BOOL finished) {

 

                [button removeTarget:self action:@selector(topButtonClick:) forControlEvents:UIControlEventTouchUpInside];

                [button addTarget:self action:@selector(bottomButtonClick:) forControlEvents:UIControlEventTouchUpInside];

 

                [button deleteImageViewHiden:YES];

                [self editeButtonGesture:button];

                

                if (self.dragBlock) {

                    self.dragBlock(_topDataArray);

                }

            }];

        } else {

            

            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"至少保留 4 个标签"

                                                            message:nil

                                                           delegate:nil

                                                  cancelButtonTitle:@"返回"

                                                  otherButtonTitles: nil];

            [alert show];

        }

    } else { // 数据回调

        if (self.tapBlock) {

            self.tapBlock(button.dataSourceModel);

        }

    }

}

 

/**

 *  底部所有button的点击方法

 *  1.移除当前点击button + 对应数据源,添加到顶部 _topButtonArray + _topDataArray

 *  2.执行移动到顶部的动画,如果此时顶部 _topButtonArray 中最后一个button独自占一行,那么要相应调整底部lable的位置 + 底部_bottomButtonArray 要重新排序,

 *  3.动画结束,移除底部button当前绑定的点击事件,添加顶部button的点击事件.此时如果是编辑(拖动)状态,那么当前点击的这个button就要显示删除按钮,反之不显示。

      并且要绑定长按事件,回调新的顺序

 *

 */

- (void)bottomButtonClick:(DragButton *)button {

   

    NSInteger index = [_bottomButtonArray indexOfObject:button];

   

    DragButtonModel *model = _bottomDataArray[index];

    [_topDataArray  addObject:model];

    [_bottomDataArray removeObject:model];

 

    [_topButtonArray addObject:button];

    [_bottomButtonArray removeObject:button];

    

    NSInteger percent = _topButtonArray.count % ButtonNumber;

    BOOL update = (percent == 1) ? YES : NO;

    

    CGFloat topY    = CGRectGetMaxY(self.topLabel.frame);

 

    [UIView animateWithDuration:0.3 animations:^{

       

        button.frame = [self getRectAtIndex:_topButtonArray.count - 1 topLocation:topY];

        [self moveToTopFirst:update];

        

    } completion:^(BOOL finished) {

        [button removeTarget:self action:@selector(bottomButtonClick:) forControlEvents:UIControlEventTouchUpInside];

        [button addTarget:self action:@selector(topButtonClick:) forControlEvents:UIControlEventTouchUpInside];

        

        if (self.editeButton.selected) {

            [button deleteImageViewHiden:NO];

        }

        

        [self editeButtonGesture:button];

 

        if (self.dragBlock) {

            self.dragBlock(_topDataArray);

        }

    }];

}

 

- (CGRect)getRectAtIndex:(NSInteger)index topLocation:(NSInteger)top {

    

    CGFloat topY       = top; // 起始高度

    CGFloat border     = 10// 控件间隙

    CGFloat viewWidth  = (SCREENWIDTH - border * 4) / ButtonNumber; // 控件宽度

    CGFloat viewHeight = 40; // 控件高度

    

    CGFloat x   = (viewWidth + border)  * (index % ButtonNumber) + border;

    CGFloat y   = (viewHeight + border) * (index / ButtonNumber) + border + topY;

    

    return CGRectMake(x, y, viewWidth, viewHeight);

}

 

// 移除 + 绑定长按事件

- (void)editeButtonGesture:(DragButton *)button {

    

    NSArray *gestures = button.gestureRecognizers;

    if (gestures.count > 0) {

        UILongPressGestureRecognizer *longGesture = gestures.firstObject;

        longGesture.delegate = nil;

        [button removeGestureRecognizer:longGesture];

        

    } else {

        UILongPressGestureRecognizer *longGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];

        [button addGestureRecognizer:longGesture];

        longGesture.minimumPressDuration = 0.2;

        longGesture.delegate = self;

        

    }

}

 

- (void)updateTopLabelTitle {

 

    self.topLabel.text = self.editeButton.selected ? @"拖动排序" : @"进入";

}

 

// 顶部 _topButtonArray 追加button(底部点击移动到顶部)

- (void)moveToTopFirst:(BOOL)firstOne {

    CGFloat topY    = CGRectGetMaxY(self.topLabel.frame);

    NSInteger index = _topButtonArray.count - 1;

       DragButton *btn = _topButtonArray[index];

    btn.frame = [self getRectAtIndex:index topLocation:topY];

    if (firstOne) { // 点击下面button(topButtonArray.count % 3 == 1,每一行的第一个)

        

        [self updateBottomLabelLocation];

    }

    [self updateBottomButtonLocation];

}

 

// 点击顶部button执行的动画:最后一个不需要重新排序,其他都要重新排序

- (void)removeFromTopIsLastOne:(BOOL)lastOne currentIndex:(NSInteger)index {

 

    if (lastOne) {  // 点击上面button 如果是最后一个_topButtonArray无需重新排序)

        

    } else { // 如果不是最后一个 _topButtonArray 后续所有需要重新排序

        CGFloat topY    = CGRectGetMaxY(self.topLabel.frame);

        for (NSInteger i = index; i < _topButtonArray.count; i++) {

            DragButton *btn = _topButtonArray[i];

            btn.frame = [self getRectAtIndex:i topLocation:topY];

        }

    }

    

    // 如果topButtonArray.count % 3 == 0(每一行的第三个button),那么要相应变动  底部label.frame    _bottomButtonArray 中的button.frame

    NSInteger percent = _topButtonArray.count % ButtonNumber;

    if (percent == 0) {

        

        [self updateBottomLabelLocation];

    }

    [self updateBottomButtonLocation];

}

 

- (void)updateTopButtonDrag:(BOOL)drag {

    for (NSInteger i = 0; i < _topButtonArray.count; i++) {

        DragButton *button = _topButtonArray[i];

        [button deleteImageViewHiden:drag];

    }

}

- (void)updateBottomLabelLocation {

    if (_topButtonArray.count > 0) {

        

        DragButton *btn = _topButtonArray[_topButtonArray.count - 1];

        _criticalValue = CGRectGetMaxY(btn.frame) + 20;

        self.bottomLabel.frame = CGRectMake(10, _criticalValue,SCREENWIDTH - 20, 30);

        self.allButton.frame   = CGRectMake(10, _criticalValue,SCREENWIDTH - 20, 30);

    }

}

- (void)updateBottomButtonLocation {

    CGFloat bottomY = CGRectGetMaxY(self.bottomLabel.frame);

    for (NSInteger i = 0; i < _bottomButtonArray.count; i++) {

        DragButton *btn = _bottomButtonArray[i];

        btn.frame = [self getRectAtIndex:i topLocation:bottomY];

    }

}

 #pragma mark - UILongPressGestureRecognizer

/**

 *  长按拖动详解 (核心代码)

 *  UIGestureRecognizerStateBegan 当前button执行放大+改变透明度的动画,并从 _topButtonArray + _topDataArray 移除,记录其最大、小Y坐标

 *  UIGestureRecognizerStateChanged button跟随手势移动

 *  1.当前拖动Y坐标 < scrollview滑动的位移,此时scrollview滚动到顶部---看效果可知

 *  2.当前拖动button与其他button交汇。

      A情况 --- 水平移动,与同一行的button交汇,根据 X 坐标判断滑动方向,并执行动画

      B情况 ---

以上是关于模仿UC新闻标签编辑功能的主要内容,如果未能解决你的问题,请参考以下文章

模仿新闻登录页面

模仿东京首页banner轮播,京东新闻上下滚动动画实现(动画实现)

模仿微信标签功能的自动换行线性布局(自定义流式标签组件)

模仿微信标签功能的自动换行线性布局(自定义流式标签组件)

[MAUI]模仿Chrome下拉标签页的交互实现

如何模仿 iPod 剩余时间和播放时间擦洗标签