项目实战No6 标签按钮

一 键盘处理相关属性

  • 建一分类,通用加载xib文件
 + (instancetype)viewFromXib
 {
    return [[[NSBundle mainBundle]    loadNibNamed:NSStringFromClass(self) owner:nil options:nil]  lastObject];
 }
  • 键盘辅助控件,控件放到键盘上,跟随键盘弹出取消
self.textView.inputAccessoryView = toolbar;
  • 键盘属性
 UIView *keyboard = [[UIView alloc] init];
 self.textView.inputView = keyboard;
  • 取消覆盖键盘,可以切换键盘,系统键盘与自定义view切换
    // 使用系统自带的键盘
    if (self.textView.inputView) {
        self.textView.inputView = nil;
        } else {
          UIView *keyboard = [[UIView alloc] init];
          self.textView.inputView = keyboard;
        }   
         [self.textView resignFirstResponder];   //  退出
         [self.textView becomeFirstResponder];
    }    
  1. 键盘辅助控件,工具条随着键盘退出而消失,不能满足需求;
  2. 想要工具条一直存在,就要成为控制器的一部分,添加到控制器view上变成子控件;
  3. 不能加到textView上,因为textView能滚动,一定要加在textView后面,盖住textView。

二 显示隐藏工具条

  • 监听键盘隐藏消失Transform
// 通知
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
// 移除通知
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
  • 工具条平移的距离
#pragma mark - 监听
- (void)keyboardWillChangeFrame:(NSNotification *)note
{
    CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    [UIView animateWithDuration:duration animations:^{
        // 工具条平移的距离 == 键盘最终的Y值 - 屏幕高度
        CGFloat ty = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y - XMGScreenH;
        self.toolbar.transform = CGAffineTransformMakeTranslation(0, ty);
    }];
}
  • 一进来键盘就是弹出状态
    [self.textView becomeFirstResponder];
  • 添加标签的+号按钮
- (void)awakeFromNib
{
    UIButton *addButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [addButton setImage:[UIImage imageNamed:@"tag_add_icon"] forState:UIControlStateNormal];
    [addButton sizeToFit];
    [addButton addTarget:self action:@selector(addClick) forControlEvents:UIControlEventTouchUpInside];
    [self.topView addSubview:addButton];
}
  • A控制器 –modal–> B控制器
    • 可以通过A.presentedViewController == B
    • 可以通过B.presentingViewController == A
- (void)addClick
{
    XMGAddTagViewController *addTag = [[XMGAddTagViewController alloc] init];
    XMGNavigationController *nav = [[XMGNavigationController alloc] initWithRootViewController:addTag];

    // 拿到"窗口根控制器"曾经modal出来的“发表文字”所在的导航控制器
    UIViewController *vc = self.window.rootViewController.presentedViewController;
    [vc presentViewController:nav animated:YES completion:nil];
}
  • 键盘弹出,viewWillAppear(界面即将出现就弹出键盘)和viewDidAppear
  - (void)viewWillAppear:(BOOL)animated
  {
    [super viewWillAppear:animated];
    [self.textView becomeFirstResponder];
   }

三 文本框占位文字和光标处理

  • 用viewDidLoad方法初始化TextField
  • 设置TextField
- (void)setupTextField
{
    UITextField *textField = [[UITextField alloc] init];
    textField.x = XMGCommonSmallMargin;
    textField.width = self.view.width - 2 * textField.x;
    textField.height = XMGTagH;
    textField.y = XMGNavBarMaxY + XMGCommonSmallMargin;
    // 设置占位文字
    textField.placeholderColor = [UIColor grayColor];
    textField.placeholder = @"多个标签用逗号或者换行隔开";
    // 必须在设置完占位文字内容以后,再通过placeholderLabel.textColor设置占位文字颜色
//    [textField setValue:[UIColor redColor] forKeyPath:XMGPlaceholderColorKey];
    [self.view addSubview:textField];

    [textField becomeFirstResponder];
    // 刷新的前提:这个控件已经被添加到父控件
    [textField layoutIfNeeded];
}
  • 占位文字颜色设置经常用,可以新建分类
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
    BOOL change = NO;
    // 保证有占位文字
    if (self.placeholder == nil) {   // 没有占位文字
        self.placeholder = @" ";
        change = YES;
    }
    // 设置占位文字颜色
    [self setValue:placeholderColor forKeyPath:XMGPlaceholderColorKey];
    // 恢复原状
    if (change) {
        self.placeholder = nil;
    }
}

四 block知识

  • 定义变量

    • 普通变量:变量类型 变量名字 = 变量的值;
    • block变量: void (^myBlock)() = ^{
      返回值 (^变量名字)(形参类型列表) = ^(形参列表) {
      // ….
      };
  • 定义属性

    • 普通属性:
      @property (nonatomic, xxx) 变量类型 变量名字;
    • block属性:@property (nonatomic, copy) int (^sumBlock)(int, int);
      @property (nonatomic, xxx) 返回值 (^变量名字)(形参类型列表);
  • 定义方法参数

 - (void)方法名:(参数类型)参数名字
 - (void)test2:(int (^)(int, int))sumBlock
 {
    // ...
 }
  • 定义block返回值
 - (返回值类型)方法名
 - (int (^)(int, int))test3
 {
    // ...
 }

五 监听文本框文字改变

  • 加一个父控件,里面放所有标签按钮、文本框等内容
- (void)setupTextField
{
    UITextField *textField = [[UITextField alloc] init];
    textField.x = XMGCommonSmallMargin;
    textField.width = self.view.width - 2 * textField.x;
    textField.height = XMGTagH;
    textField.y = XMGNavBarMaxY + XMGCommonSmallMargin;
    // 设置占位文字
    textField.placeholderColor = [UIColor grayColor];
    textField.placeholder = @"多个标签用逗号或者换行隔开";
    // 必须在设置完占位文字内容以后,再通过placeholderLabel.textColor设置占位文字颜色
//    [textField setValue:[UIColor redColor] forKeyPath:XMGPlaceholderColorKey];
    [self.view addSubview:textField];

    [textField becomeFirstResponder];
    // 刷新的前提:这个控件已经被添加到父控件
    [textField layoutIfNeeded];
}
  • 代理监听标签文字改变
#pragma mark - <UITextFieldDelegate>
/**   监听用户的输入
 是否应该用string替换range范围内的字符串
 @param range  光标的范围(或者文字的选中范围)
 @param string 用户此时输入的文字
 @return YES:允许利用string替换range范围内的字符串, NO:不允许....
 */
 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
 {
    return YES;
 }
  • 上面监听方法不完整,只能监听键盘正常输入,上面内容监听不到
    • 想要完整监听换textDidChange方法
 /**
  监听textField的文字改变
 */
 - (void)textDidChange
 {
    // 提醒按钮
    if (self.textField.hasText) {
        self.tipButton.hidden = NO;   // 显示回来
        self.tipButton.y = CGRectGetMaxY(self.textField.frame) + XMGCommonSmallMargin;
        [self.tipButton setTitle:[NSString stringWithFormat:@"添加标签:%@", self.textField.text] forState:UIControlStateNormal];
    } else {
        self.tipButton.hidden = YES;
    }
 }
  • 每次输入都会创建新按钮,搞个懒加载
- (UIButton *)tipButton
{
    if (!_tipButton) {
        // 创建一个提醒按钮
        UIButton *tipButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [tipButton addTarget:self action:@selector(tipClick) forControlEvents:UIControlEventTouchUpInside];
        tipButton.width = self.contentView.width;
        tipButton.height = XMGTagH;
        tipButton.x = 0;
        tipButton.backgroundColor = XMGTagBgColor;
        tipButton.titleLabel.font = [UIFont systemFontOfSize:14];
//        tipButton.titleLabel.textAlignment = NSTextAlignmentLeft;
//        tipButton.titleLabel.backgroundColor = XMGRandomColor;
        tipButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
        tipButton.contentEdgeInsets = UIEdgeInsetsMake(0, XMGCommonSmallMargin, 0, 0);
        [self.contentView addSubview:tipButton];
        _tipButton = tipButton;
    }
    return _tipButton;
}

六 添加标签按钮

  • 调整Label居左
  tipButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
  • 自定义标签按钮
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = XMGTagBgColor;
        [self setImage:[UIImage imageNamed:@"chose_tag_close_icon"] forState:UIControlStateNormal];
        self.titleLabel.font = [UIFont systemFontOfSize:14];
    }
    return self;
}
  • 实现按钮比较小,sizetofit根据文字内容调,重写setTitle
    • 按钮间距调整
- (void)setTitle:(NSString *)title forState:(UIControlState)state
{
    [super setTitle:title forState:state];
    // 自动计算
    [self sizeToFit];

    // 微调
    self.height = XMGTagH;
    self.width += 3 * XMGCommonSmallMargin;
}
  • image图片与文字重新布局
- (void)layoutSubviews
{
    [super layoutSubviews];

    self.titleLabel.x = XMGCommonSmallMargin;

    self.imageView.x = CGRectGetMaxX(self.titleLabel.frame) + XMGCommonSmallMargin;
}
  • 点击提醒按钮
/**
 点击了提醒按钮
 */
- (void)tipClick
{
    // 如果没有文字,直接返回
    if (self.textField.hasText == NO) return;
    // 创建一个标签按钮
    XMGTagButton *newTagButton = [XMGTagButton buttonWithType:UIButtonTypeCustom];
    [newTagButton setTitle:self.textField.text forState:UIControlStateNormal];
    [self.contentView addSubview:newTagButton];
    // 设置位置
    // 最后一个标签按钮
    UIButton *lastTagutton = self.tagButtons.lastObject;
    if (lastTagutton) { // 不是第一个标签
        // 左边的总宽度
        CGFloat leftWidth = CGRectGetMaxX(lastTagutton.frame) + XMGCommonSmallMargin;
        // 右边剩下的宽度
        CGFloat rightWidth = self.contentView.width - leftWidth;
        if (rightWidth >= newTagButton.width) { // 跟最后一个按钮处在同一行
            newTagButton.x = leftWidth;
            newTagButton.y = lastTagutton.y;
        } else { // 下一行
            newTagButton.x = 0;
            newTagButton.y = CGRectGetMaxY(lastTagutton.frame) + XMGCommonSmallMargin;
        }
    } else { // 第一个标签按钮
        newTagButton.x = 0;
        newTagButton.y = 0;
    }
    // 添加到数组中
    [self.tagButtons addObject:newTagButton];  
}
  • 排布文本框
// 排布文本框
    CGFloat leftWidth = CGRectGetMaxX(newTagButton.frame) + XMGCommonSmallMargin;
    self.textField.y = newTagButton.yself.textField.text = nil;

    // 隐藏提醒按钮
    self.tipButton.hidden = YES;
  • 判断文本框位置
    • 如果间距小于100,文本框换行
 CGFloat rightWidth = self.contentView.width - leftWidth;
    if (rightWidth >= 100) { // 跟新添加的标签按钮处在同一行
        self.textField.x = leftWidth;
        self.textField.y = newTagButton.y;
    } else { // 换行
        self.textField.x = 0;
        self.textField.y = CGRectGetMaxY(newTagButton.frame) + XMGCommonSmallMargin;
    }
  • 监听键盘return点击
#pragma mark - <UITextFieldDelegate>
/**
 点击右下角return按钮就会调用这个方法
 */
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [self tipClick];
    return YES;
}
  • Text Field有Return Key属性,指定键盘右下键按键属性
    这里写图片描述

    • 默认Default,根据键盘类型不同值不同(换行【中文】、return)
  • 监听变化

/**
 监听textField的文字改变
 */
- (void)textDidChange
{
    // 提醒按钮
    if (self.textField.hasText) {
        NSString *text = self.textField.text;
        NSString *lastChar = [text substringFromIndex:text.length - 1];
        if ([lastChar isEqualToString:@","]
            || [lastChar isEqualToString:@","]) { // 最后一个输入的字符是逗号
            // 去掉文本框最后一个逗号
            self.textField.text = [text substringToIndex:text.length - 1];
            // 点击提醒按钮
            [self tipClick];
        } else { // 最后一个输入的字符不是逗号
            CGFloat textW = [text sizeWithAttributes:@{NSFontAttributeName : self.textField.font}].width;
            // 排布文本框
            XMGTagButton *lastTagButton = self.tagButtons.lastObject;
            CGFloat leftWidth = CGRectGetMaxX(lastTagButton.frame) + XMGCommonSmallMargin;
            CGFloat rightWidth = self.contentView.width - leftWidth;
            if (rightWidth >= textW) { // 跟新添加的标签按钮处在同一行
                self.textField.x = leftWidth;
                self.textField.y = lastTagButton.y;
            } else { // 换行
                self.textField.x = 0;
                self.textField.y = CGRectGetMaxY(lastTagButton.frame) + XMGCommonSmallMargin;
            }

            self.tipButton.hidden = NO;
            self.tipButton.y = CGRectGetMaxY(self.textField.frame) + XMGCommonSmallMargin;
            [self.tipButton setTitle:[NSString stringWithFormat:@"添加标签:%@", text] forState:UIControlStateNormal];
        }
    } else {
        self.tipButton.hidden = YES;
    }
}

七 删除标签

  • 监听删除点击
[newTagButton addTarget:self action:@selector(tagClick) forControlEvents:UIControlEventTouchUpInside];
  • 点击哪个标签删除哪个标签(索引)
    • 判断按钮索引,判断第一个按钮与其他位置按钮删除
/**
 点击了标签按钮
 */
- (void)tagClick:(XMGTagButton *)clickedTagButton
{
    // 即将被删除的标签按钮的索引
    NSUInteger index = [self.tagButtons indexOfObject:clickedTagButton];

    // 删除按钮
    [clickedTagButton removeFromSuperview];
    [self.tagButtons removeObject:clickedTagButton];  //  数组

    // 处理后面的标签按钮
    for (NSUInteger i = index; i < self.tagButtons.count; i++) {
        XMGTagButton *tagButton = self.tagButtons[i];
        // 如果i不为0,就参照上一个标签按钮
        XMGTagButton *previousTagButton = (i == 0) ? nil : self.tagButtons[i - 1];
        [self setupTagButtonFrame:tagButton referenceTagButton:previousTagButton];
    }
    // 排布文本框
    [self setupTextFieldFrame];
}

文本框需要跟随删除按钮上移,删除完按钮标签,注意需要重新排布文本框。

#pragma mark - 设置控件的frame
/**
 * 设置标签按钮的frame
 * @param tagButton 需要设置frame的标签按钮
 * @param referenceTagButton 计算tagButton的frame时参照的标签按钮
 */
- (void)setupTagButtonFrame:(XMGTagButton *)tagButton referenceTagButton:(XMGTagButton *)referenceTagButton
{
    // 没有参照按钮(tagButton是第一个标签按钮)
    if (referenceTagButton == nil) {
        tagButton.x = 0;
        tagButton.y = 0;
        return;
    }
    // tagButton不是第一个标签按钮
    CGFloat leftWidth = CGRectGetMaxX(referenceTagButton.frame) + XMGCommonSmallMargin;
    CGFloat rightWidth = self.contentView.width - leftWidth;
    if (rightWidth >= tagButton.width) { // 跟上一个标签按钮处在同一行
        tagButton.x = leftWidth;
        tagButton.y = referenceTagButton.y;
    } else { // 下一行
        tagButton.x = 0;
        tagButton.y = CGRectGetMaxY(referenceTagButton.frame) + XMGCommonSmallMargin;
    }
}

八 键盘删除键点击

  • 监听删除键点击
/**
 * 监听键盘内部的删除键点击
 */
- (void)deleteBackward
{
    // 执行需要做的操作
    !self.deleteBackwardOperation ? : self.deleteBackwardOperation();
    [super deleteBackward];
}

注意:代码调用顺序,防止删除最后一个字时标签会跟随一起删除!

  • 删除最后一个按钮标签,需要把控制器的删除功能代码传到TagTextField.m中,用block方法

    • 定义block

      /** 点击删除键需要执行的操作 */
      @property (nonatomic, copy) void (^deleteBackwardOperation)();
    • 需要执行的操作

      self.deleteBackwardOperation();
    • block存储代码

  // 设置点击删除键需要执行的操作
    textField.deleteBackwardOperation = ^{
        // 判断文本框是否有文字
     if (weakSelf.textField.hasText) return;
        // 点击了最后一个标签按钮(删掉最后一个标签按钮)
     [weakSelf tagClick:weakSelf.tagButtons.lastObject];
    };
  • 控制标签数量
   if (self.tagButtons.count == 5) {
[SVProgressHUD showErrorWithStatus:@"最多添加5个标签" maskType:SVProgressHUDMaskTypeBlack];
   return;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值