我曾經遇到過一個問題:需要實現一個自定義的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.";