我曾经遇到过一个问题:需要实现一个自定义的label(类似于UILabel),同时需要兼顾UILabel的大小自适应的特性。这个label通常宽度是固定的,通过autolayout指定其宽度约束,但不指定高度,让其根据内容自适应。
我们知道UIView的方法intrinsicContentSize可以帮助我们确定视图在autolayout下的大小,从而避免我们去设置其宽高的约束。于是我采用了这样的解决方案:将label的宽度作为其属性,使用前我必须指定label的宽度,然后label本身通过其宽度属性确定高度。然而这个方法不太方便,因为宽度有时需要我们去计算,比如说它是屏幕宽度减去某个值。然而我是一个足够懒的程序员,不想每次去手动设置宽度。
后来思路终于有了,虽然不知道这是否是最佳方法,但至少解决了问题。在autolayout布局完成后,我们就可以知道视图的最终宽度了。这时我们可以通过
invalidateIntrinsicContentSize方法重新计算视图的大小。
代码如下:
1 @interface MyLabel : UIView 2 3 @property (nonatomic, strong) UIFont *font; 4 @property (nonatomic, strong) NSString *text; 5 6 @end
1 @interface MyLabel () 2 3 @property (nonatomic, strong) UIFont *defaultFont; 4 5 @end 6 7 @implementation MyLabel 8 9 - (instancetype)init 10 { 11 self = [super init]; 12 if (self) { 13 self.defaultFont = [UIFont systemFontOfSize:16]; 14 } 15 return self; 16 } 17 18 - (void)setText:(NSString *)text 19 { 20 _text = text; 21 [self setNeedsLayout]; 22 [self setNeedsDisplay]; 23 } 24 25 - (void)layoutSubviews 26 { 27 [self invalidateIntrinsicContentSize]; // 在布局的时候强制重新计算大小 28 [super layoutSubviews]; 29 } 30 31 - (CGSize)sizeThatFits:(CGSize)size 32 { 33 UIFont *font = self.font ? self.font : self.defaultFont; 34 size.width = ceil(size.width); // 避免单行文本二次计算时被错误地计算为两行高度 35 if (size.width > 0) { 36 return [self.text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:font} context:nil].size; 37 } else { // 此处是为了兼容不设宽度的情况(单行文本) 38 return [self.text sizeWithAttributes:@{NSFontAttributeName:font}]; 39 } 40 } 41 42 - (CGSize)intrinsicContentSize 43 { 44 return [self sizeThatFits:CGSizeMake(CGRectGetWidth(self.bounds), CGFLOAT_MAX)]; 45 } 46 47 - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize 48 { 49 return [self sizeThatFits:targetSize]; 50 } 51 52 - (void)drawRect:(CGRect)rect 53 { 54 UIFont *font = self.font ? self.font : self.defaultFont; 55 [self.text drawInRect:rect withAttributes:@{NSFontAttributeName:font}]; 56 } 57 58 @end
让我们来测试一下:
1 MyLabel *label = [MyLabel new]; 2 label.backgroundColor = [UIColor yellowColor]; 3 [self.view addSubview:label]; 4 [label mas_makeConstraints:^(MASConstraintMaker *make) { // 这里并没设高度约束 5 make.left.mas_equalTo(10); 6 make.top.mas_equalTo(100); 7 make.width.mas_lessThanOrEqualTo(100); 8 }]; 9 10 label.text = @"this is a long text. this is a long text.";