继续记录6月份开发中遇到的问题。
UIBarButtonItem点击区域的问题
今天在定制导航上的返回按钮遇到了一个问题,顺便记录一下。
一般常规的定制导航栏返回按钮的方法如下:
1 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"back"] style:UIBarButtonItemStyleDone target:self action:@selector(clickBackBtn:)]; |
这样写有一个问题,运行代码后,返回按钮的可点击区域如下图所示:
这并不是我想要的,点击区域太大,完全没有点到返回按钮上都可以触发返回操作。于是换一种思路,用自定义View实现,代码如下:
1 | UIButton *backBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 44)]; |
编译运行,效果依然一样,点击区域还是太大,如下图:
这我就不明白了,于是一阵Google,找到了一个解决方案,代码如下:
1 | UIButton *backBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 44)]; |
这样点击区域就正常了,如下图:
其实就是在Button外面又包了一层View,这样点击区域就只有Button那么大,我怀疑,可能是加载BarButtonItem上的自定义View都会被自动的拉长,暂时还没找到其他解决方案。
Reference:UIBarButtonItem tap area
将自定义对象作为NSDictionary的Key
将自定义对象作为NSDictionary的Key,需要重写自定义isEqual方法和hash方法。isEqual方法比较简单,如下代码:
1 | - (BOOL)isEqual:(id)object { |
但是如果要把自定义的对象作为NSDictionary的Key,光重写isEqual方法是不够的,还需要重写hash方法,如果不重写hash方法就会导致字典不能正确的添加和删除。重写hash函数的方法很多,可以通过构建不同散列函数来实现,但是无论哪种方法,目的都是尽可能的确保相同的对象返回的hash值相同,一个简单的方法就是使用每一个属性的hash的异或的结果来作为这个对象的hash值。代码如下:
1 | - (NSUInteger)hash { |
这样构建的hash值冲突的概率很小。另外,要作为字典的Key,还必须实现NSCopying
协议。因为不需要做深拷贝,所以只需要简单的返回self即可,代码如下:
1 | - (id)copyWithZone:(NSZone *)zone { |
这个构建的一个对象就可以作为NSDictionary的Key了。
Reference:Equality
UITableViewCell的“no index path for table cell being reused”问题
项目中有一个自定义的UITableViewCell,由于版本更新,要把这个Cell改为可以悬停的SectionHeader,为了图方便,我没有重新自定义一个View,而是直接在viewForHeaderInSection:
方法里返回了cell,代码如下:
1 | LMBookWriteCommentCell *cell = (LMBookWriteCommentCell *)[self tableView:self.tableView writeCommentCellWithIndexPath:nil]; |
这样写有个问题,就是在reloadData时,这SectionHeader就消失了,变成了空白的一片,并且出现了“no index path for table cell being reused”错误,程序没有崩溃,但是就是看不见Cell上的内容。于是一阵Google,终于找到了答案,只要把上面的代码的return cell;
改为return cell.contentView;
就可以了。
Reference:What is the meaning of the “no index path for table cell being reused” message in iOS 6/7?
Autolayout动画的正确姿势
做Autolayout动画的一般步骤是:
- 修改约束,如果使用Masonry,使用
mas_updateConstraints
方法修改约束,如果使用Xib,则直接把要修改的约束通过拉线链接到代码里进行修改即可。 - 在动画的block中,调用View的
layoutIfNeed()
方法。
这里要注意的一点是第二步,如果直接调用view.layoutIfNeed()
,可能会导致动画不能进行,正确的方法应该是调用view.superview.layoutIfNeed()
,这样才能正确的进行动画。完整代码如下:
1 | class ViewController: UIViewController { |
在iOS7上UILabel的省略点颜色不正确
在iOS7上,有时UILabel的省略点的颜色与文字的颜色不同,如下图:
网上查了一下,这应该是iOS7的一个Bug,解决方法就是不要使用textColor设置文字颜色,而使用attributeText,代码如下:
1 | NSAttributedString *attStr = [[NSAttributedString alloc] initWithString:self.titleLabel.text attributes:@{NSForegroundColorAttributeName:style.fColor, NSFontAttributeName:style.font}]; |
参考:UILabel dotted line color bug in iOS 7.1
iOS7上,给UICollectionViewCell添加长按手势的坑
项目中有一个需求,当长按任何一个UICollectionViewCell时,刷新UICollectionView,每一个Cell的右上角都出现一个选择按钮。效果如下:
这是一个比较常见的需求,我的做法是在每一个Cell上添加一个长按手势,然后,在手势触发时,刷新这个UICollectionView,然每一个Cell都显示选择按钮。这样做在iOS8,iOS9上都没有问题,当时在iOS7上却出现了问题。在iOS7上,当触发某一个Cell的长按手势之后,这个Cell就不再响应UICollectionView的collectionView:didSelectItemAtIndexPath:
方法了。
网上没有找到类似的情况,只能自己解决。最终找到了一种解决方案,代码如下:
1 | // 必须重新放在主线程队列中,否则在iOS7上回出现Cell不触发didSelect的情况 |
要将reloadData重新放在主线程队列中,这样就不会出现这种情况。具体原因不是非常明白,但是,这样确实可以完美的解决这个问题。