【问题标题】:iOS - Multiline UILabel Height With ConstraintsiOS - 带约束的多行 UILabel 高度
【发布时间】: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;
}

【问题讨论】:

    标签: ios objective-c uilabel autolayout nslayoutconstraint


    【解决方案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:8794Auto 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 以响应标签大小变化的问题。我的意思是,如果这是您想要做的,您需要以某种方式手动执行此操作。是的,如果您使用自动布局,则必须使用约束来完成,而不是直接设置框架。
    猜你喜欢
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    • 2020-05-30
    • 2023-03-17
    • 2015-01-30
    相关资源
    最近更新 更多