iOS – 自定义UIButton图文布局 + LongPress多次响应问题

本文记录两个问题:
1. 自定义UIButton,例如实现上图片下文字,或者左文字右图片效果
2. UILongPressGestureRecognizer 执行两次问题。(由一个长按评论回复功能引发的Bug)

自定义UIButton

在跟小码哥实战课程做项目时,小码哥经常会使用到layoutSubviews的方式去自定义按钮布局,最近又接触到另一种自定义按钮的方式,可以方便的实现上图片下文字,或者左文字右图片效果。

下面两种方法都是需要自定义一个Button类,继承UIButton

1.重写layoutSubviews:

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    // 调整图片
    self.imageView.y = self.height * 0.15;
    self.imageView.width = self.width * 0.5;
    self.imageView.height = self.imageView.width;
    self.imageView.centerX = self.width * 0.5;
    
    // 调整文字
    self.titleLabel.x = 0;
    self.titleLabel.y = CGRectGetMaxY(self.imageView.frame);
    self.titleLabel.width = self.width;
    self.titleLabel.height = self.height - self.titleLabel.y;
}

在该方法中必须先调用[super layoutSubviews]; 不然可能会看不到image和label。

2.重写 RectForContentRect:(CGRect)contentRect 方法

先看一个例子:

// 返回标题边界
-(CGRect)titleRectForContentRect:(CGRect)contentRect{
      // 这contentRect就是button的frame,我们返回标题view宽和button相同,高为20,在button的底部
      return CGRectMake(0, contentRect.size.height-20, contentRect.size.width, 20);
}

// 返回图片边界
-(CGRect)imageRectForContentRect:(CGRect)contentRect{
      // 获取原始Image的frame 
      CGRect frame = [super imageRectForContentRect:contentRect];
      frame.origin = CGPointMake(10,20);
      
      return frame;
}

可以通过重写如下方法来修改frame
-(CGRect)titleRectForContentRect:(CGRect)contentRect
-(CGRect)imageRectForContentRect:(CGRect)contentRect

注意: 如果想要在 titleRectForContentRect 方法中通过titleLabel.text 计算宽高灯操作,不可直接使用self.titleLabel.text获取文字,因为这种方式会自动调用 titleRectForContentRect ,从而造成死循环,可以使用 self.currentTitle获取文字。


UILongPressGestureRecognizer 执行两次问题

问题还原:
现在我们有一个需求,当长按某一个评论cell时表示要回复该评论,需要底部弹出内容输入框。

于是采用下面的方式给cell添加长按手势

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // ...
    
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressCell:)];
    [cell addGestureRecognizer:longPress];
    
    return cell;
}

// 点击事件
- (void)longPressWithComment:(CommentModel *)comment {
    [self pressCommentBtnWithType:kCommentTypeComment contentID: _dataSource.blog_id parentID:comment.comment_id];
}

当长按评论进行回复时,会弹出多个输入框,甚至在长按移动手指时也会弹出多个。

后经查证,LongPress有多个状态,如Began, Changed, Ended, 所以当长按时,长按开始会调用一次点击事件,长按结束会再次调用一次,甚至在移动时(changed)也会调用。

所以LongPress的点击事件不能和TapGesture一样简单了,需要加状态判断,

// 点击事件
- (void)longPressWithComment:(CommentModel *)comment {
    if (gestrue.state == UIGestureRecognizerStateEnded) {
        [self pressCommentBtnWithType:kCommentTypeComment contentID: _dataSource.blog_id parentID:comment.comment_id];
    }
}