iOS - 带约束的多行 UILabel 高度
Posted
技术标签:
【中文标题】iOS - 带约束的多行 UILabel 高度【英文标题】:iOS - Multiline UILabel Height With Constraints 【发布时间】:2014-08-02 22:30:01 【问题描述】:我正在尝试创建一个可重用的消息UIView
子类,该子类根据其UILabel
中的文本调整其高度。这个答案说它不能完成。真的是这样吗? ios message cell width/height using auto layout
我的问题是 UILabel 的 CGFrame
的高度太大。 (UILabel
的背景颜色为绿色。)
这是我的代码(顺便说一下,[autolayoutView]
将 translatesAutoresizingMaskIntoConstraints
设置为 NO
):
SSLStickyView *stickyView = [[SSLStickyView alloc] initWithText:@"Try keeping a steady beat to help me get past the notes! Press the bass drum to jump!"];
SSLStickyView.m
- (instancetype)initWithText:(NSString *)text
self = [super initWithFrame:CGRectZero];
if (self)
_stickyImageView = [UIImageView autoLayoutView];
_stickyImageView.backgroundColor = [UIColor blueColor];
_stickyImageView.image = [UIImage imageNamed:@"element_sticky"];
[self addSubview:_stickyImageView];
float padding = 5;
NSMutableAttributedString *attributedText =
[[NSMutableAttributedString alloc]
initWithString:text
attributes:@
NSFontAttributeName: [UIFont boldSystemFontOfSize:30],
NSForegroundColorAttributeName: [UIColor purpleColor]
];
UILabel *textLabel = [UILabel autoLayoutView];
textLabel.preferredMaxLayoutWidth = 50;
textLabel.attributedText = attributedText;
textLabel.numberOfLines = 0; // unlimited number of lines
textLabel.lineBreakMode = NSLineBreakByWordWrapping;
textLabel.backgroundColor = [UIColor greenColor];
[_stickyImageView addSubview:textLabel];
NSLayoutConstraint *stickyWidthPin =
[NSLayoutConstraint constraintWithItem:_stickyImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:textLabel
attribute:NSLayoutAttributeWidth
multiplier:1
constant:padding * 2];
NSLayoutConstraint *stickyHeightPin =
[NSLayoutConstraint constraintWithItem:_stickyImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:textLabel
attribute:NSLayoutAttributeHeight
multiplier:1
constant:0];
NSLayoutConstraint *stickyTextLabelTop =
[NSLayoutConstraint constraintWithItem:_stickyImageView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:textLabel
attribute:NSLayoutAttributeTop
multiplier:1
constant:0];
NSLayoutConstraint *stickyTextLeftPin = [NSLayoutConstraint constraintWithItem:_stickyImageView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:textLabel
attribute:NSLayoutAttributeLeft
multiplier:1
constant:-padding * 2];
NSDictionary *views = NSDictionaryOfVariableBindings(_stickyImageView, textLabel);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_stickyImageView]" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_stickyImageView]" options:0 metrics:nil views:views]];
[self addConstraints:@[stickyWidthPin, stickyHeightPin, stickyTextLeftPin, stickyTextLabelTop]];
self.backgroundColor = [UIColor whiteColor];
return self;
【问题讨论】:
【参考方案1】:让您的 superview 了解其内容大小的变化,并相应地调整 superview 的宽度和高度。由于如何做到这一点可能不是很明显,我提供了一个 UIView 子类,它将根据其内容(一个 UILabel)调整自身大小。
注意:仅向 ReusableMessageView 添加会影响其在其父视图中的位置的约束。 ReusableMessageView 将根据消息调整其宽度/高度。
@interface ReusableMessageView : UIView
-(instancetype)initWithMessage:(NSString *)message preferredWidth:(CGFloat)width;
-(void)setMessage:(NSString *)message;
@end
@implementation ReusableMessageView
UILabel *_label;
-(instancetype)initWithMessage:(NSString *)message preferredWidth:(CGFloat)width
if (self = [super init])
self.translatesAutoresizingMaskIntoConstraints = NO;
//setup label
_label = [UILabel new];
_label.translatesAutoresizingMaskIntoConstraints = NO;
_label.text = message;
_label.preferredMaxLayoutWidth = width;
_label.numberOfLines = 0;
[self addSubview:_label];
return self;
-(void)layoutSubviews
[super layoutSubviews];
// remove all previously added constraints
[self removeConstraints:self.constraints];
CGFloat width = _label.bounds.size.width;
CGFloat height = _label.bounds.size.height;
NSLayoutConstraint *c1,*c2,*c3,*c4;
// set the view's width/height to be equal to the label's width/height
c1 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:width];
c2 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:height];
// center the label
c3 = [NSLayoutConstraint constraintWithItem:_label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
c4 = [NSLayoutConstraint constraintWithItem:_label attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
// add all constraints
[self addConstraints:@[c1,c2,c3,c4]];
-(void)setMessage:(NSString *)message
_label.text = message;
// once the message changes, the constraints need to be adjusted
[self setNeedsLayout];
@end
这可以通过重用现有约束并仅更改每个约束的“常量”属性来改进。如果您这样做,您甚至可以为更改设置动画。
这是一个在 viewController 的 viewDidLoad 方法中使用的示例:
ReusableMessageView *view = [[ReusableMessageView alloc]initWithMessage:@"This is the first message that is rather long in order to exaggerate the change in size" preferredWidth:50.0];
[self.view addSubview:view];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
// demonstrate the change in size
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
[view setMessage:@"This is the second message"];
);
【讨论】:
将代码复制到新项目(在viewDidAppear
中调用)时出现以下错误:*** Assertion failure in -[RLReusableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2935.137/UIView.m:8794
Auto Layout still required after executing -layoutSubviews. RLReusableView's implementation of -layoutSubviews needs to call super.
有趣。你确定你在 RLReusableView 的 layoutSubviews 方法中实现了 [super layoutSubviews]?您是否覆盖任何其他方法?
是的,这很有趣。我确定我实现了 [super layoutSubviews]。我也发现了这个问题,但我找不到不特定于 UITableViewCell 的答案。有趣的是,我可以将 layoutSubviews 中的所有约束代码移动到 init 中,并且它可以正常工作。奇怪,不是吗?【参考方案2】:
当然可以,但不能没有代码。视图不会根据其内部内容的约束和大小自动变小。这不是自动布局的行为方式。
您可以根据其中的约束和大小(使用systemLayoutFittingSize
)确定某物需要的大小,并且您可以设置该东西代码中的那个大小。但这不会通过某种魔法发生。
【讨论】:
你的意思是这样的吗?textLabel.frame = CGRectMake(textLabel.frame.origin.x, textLabel.frame.origin.y, [textLabel systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].width, [textLabel systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height);
什么时候调用?而且我觉得我上面的代码是错误的,因为它设置了框架,并且在使用自动布局时设置框架通常是禁止的。
textLabel
将自行调整大小;无需设置其大小。我说的是你问的关于如何调整 包含 textLabel 的 UIView 以响应标签大小变化的问题。我的意思是,如果这是您想要做的,您需要以某种方式手动执行此操作。是的,如果您使用自动布局,则必须使用约束来完成,而不是直接设置框架。以上是关于iOS - 带约束的多行 UILabel 高度的主要内容,如果未能解决你的问题,请参考以下文章
boundingRectWithSize 为多行标签返回错误的高度值