需求:
实现一个多行文本框,带有提示语。
实现:
一、思路
1、一个UILabel,添加到UITextView上
2、调整UILabel,使之能和UITextView同步
(完)
二、实现
1、坑
(1)UILabel有边距
因为一般的UILabel都是有边距的,所以如果直接使用,就会造成上述的错位问题,即使你及时隐藏了,肉眼会感觉到一些跳动,用户体验不好。那么怎么办呢?
方法一:添加换行
利用sizeWithAttributes:方法计算出一行的高度,然后再用方法boundingRectWithSize:options: attributes:context:计算出字符串所要占取的高度,那么视图总高度减去文本总高度,再除以一行高度就是需要添加的换行数。
方法二:垂直方向顶部对齐(不是水平左对齐)
计算出内容实际的布局和占用大小,然后稍微处理即可。
那么怎么获取内容的实际占用位置呢?查看文档接口,有这么几句话:
// override points. can adjust rect before calling super.
// label has default content mode of UIViewContentModeRedraw
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines;
- (void)drawTextInRect:(CGRect)rect;
可以在子类中通过重写下面的方法,可以调整位置。
下面是具体的实现:
@implementation ZPZLabel
- (void)setVerticalAlignment:(ZPZLabelVerticalTextAlignment)verticalAlignment {
_verticalAlignment = verticalAlignment;
[self setNeedsDisplay];
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
CGRect actualRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
switch (_verticalAlignment) {
case ZPZLabelVerticalTextAlignmentTop:{
actualRect.origin.y = bounds.origin.y;
}
break;
case ZPZLabelVerticalTextAlignmentMiddle:{
actualRect.origin.y = bounds.origin.y + bounds.size.height / 2 - actualRect.size.height / 2;
}
break;
case ZPZLabelVerticalTextAlignmentBottom:{
actualRect.origin.y = bounds.origin.y + bounds.size.height - actualRect.size.height;
}
break;
default:
break;
}
return actualRect;
}
//这里小心写错
- (void)drawTextInRect:(CGRect)rect {
NSLog(@"%s",__func__);
CGRect actualRect = [self textRectForBounds:rect limitedToNumberOfLines:self.numberOfLines];
[super drawTextInRect:actualRect];
NSLog(@"%s",__func__);
}
//先调用父类的这个方法,然后是上面的方法,最后是该方法剩下的
- (void)drawRect:(CGRect)rect {
NSLog(@"%s",__func__);
[super drawRect:rect];
}
@end
这里需要注意的是不要写成了- (void)drawRect:(CGRect)rect。
具体的代码看这里
这里的实现采取第二种方法
(2)UITextView的默认字体
在iOS11下(其他的没试过),其font不存在。
2、实现
注意点:去除内边距
一般都是手动写死的,凡是写死的都会留下祸根!相信我,出来混,迟早都要还的。所以不建议写死!!!那怎么处理呢?
我们曾在这里对UITextView做了简单的探索,我们知道其内容是放在一个NSTextContainer里的,它里面的内容不是完全靠边的,所以这里除了要把UILabel里的内容调整为垂直顶部放置,UILabel的位置还要以这些做参考,代码实现如下:
- (void)adjustPlaceLabelFrame {
UIEdgeInsets insets = self.textContainerInset;
// NSLog(@"%@",NSStringFromUIEdgeInsets(insets));
_placeHoldLabel.frame = CGRectMake(insets.left + self.textContainer.lineFragmentPadding, insets.top, self.frame.size.width - insets.left - insets.right - self.textContainer.lineFragmentPadding, self.frame.size.height - insets.top - insets.bottom);
_placeHoldLabel.verticalAlignment = ZPZLabelVerticalTextAlignmentTop;
}
这里只做了简单的实现,基本使用应该是够用了,在后面的使用过程中会不断做完善!
效果如下:
至于什么条件消失和展示,相信很容易控制,代码里已经实现了!
另外,代码里将代理包装成了block,所以可以通过block直接使用了!不够的,可自行添加!