如何创建支持带图像背景的动态多行文本的UIButton?

我正在尝试在iOS上创建一个扩展或收缩以适应动态(通常是多行文本)的UI元素。 我需要在文本下方放置一个看起来很像消息泡泡的图像。 但是,我只需要在我的整个UI中使用其中一个,因此表格视图似乎有点过分 – 除非合理必要。

我试图将UILabel用于此目的,但似乎对背景图像的支持非常有限。 我只能得到它来平铺图像,这不是我想要的。

似乎UIButton是最简单的构造,应该合理地做我要求的,但我不能让它工作。 这是一个空的单一视图应用程序中的代码的提炼forms。

- (void)viewDidLoad { [super viewDidLoad]; NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; UIImage *image = [[UIImage imageNamed:@"speak_bubble_solid"] resizableImageWithCapInsets:UIEdgeInsetsMake(16, 24, 30, 12)]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button setBackgroundImage:image forState:UIControlStateNormal]; [button setTitle:text forState:UIControlStateNormal]; button.titleLabel.textColor = [UIColor whiteColor]; button.titleLabel.textAlignment = NSTextAlignmentLeft; button.titleLabel.numberOfLines = 0; button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping; button.contentEdgeInsets = UIEdgeInsetsMake(16, 10, 30, 10); button.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:button]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[button]-|" options:NSLayoutFormatAlignAllTop metrics:0 views:NSDictionaryOfVariableBindings(button)]]; } 

这是我运行应用程序时的样子:

截图

显然,我正在寻找的是扩展以包含其内容的按钮。 因此,即使是初始渲染也是错误的,但是当我稍后动态更改文本时,我还需要正确resize。

我该如何创建呢? 我需要在代码中看到答案,因为我在iOS开发中仍然很新。

实际上我会用的是标签和图像。 不是背景图像:图像,在图像视图中。

  • 使标签包裹,并根据其内容的大小垂直增长或收缩。 这很容易,是一个现成的,以前解决的问题。

  • 使标签的背景清晰。

  • 将图像视图约束到与其后面的标签相同的位置和大小。

  • 确保图像视图的内容模式是按比例填充,以便图像的大小始终与图像视图的大小完全相同。

  • 为图像视图提供“可伸缩”的图像,使其无论图像视图的大小如何都会看起来正确。

以下是我尝试使用不同长度的自包装标签文本时所获得的一些屏幕截图:

在此处输入图像描述

在此处输入图像描述

这看起来非常接近你所追求的东西。 在我的实现中,棘手的部分是调整图像视图的大小/位置以响应标签大小的变化,因为它包装并改变其高度以响应其内容。 我的工作方式是设置从图像视图到标签的约束,使图像视图比标签大,以便气球出现在标签的外面; 然后,我将此代码添加到我的视图控制器,以相对于标签( lab )的布局进一步实时调整图像视图( iv ):

 override func viewDidLayoutSubviews() { iv.bounds.size.height = lab.bounds.size.height + 60 iv.frame.origin.y = lab.frame.origin.y - 20 } 

编辑 :但实际上不需要代码; 它可以完全用约束来完成,正如我在这个github示例中演示的那样: https : //github.com/mattneub/MessageBubble

另一种可能的方法是使用绘制的形状而不是图像作为背景。 这需要比Matt方法更多的代码,但如果你想改变点的颜色或形状,可能会给你更大的灵活性。 我创建了一个名为RDLabelView的UIView类,它绘制了背景形状,并添加了一个标签作为子视图。 它有一个text属性,因此您可以像设置标签一样设置文本。

 #import "RDLabelView.h" @interface RDLabelView () @property (strong,nonatomic) UILabel *label; @property (strong,nonatomic) CAShapeLayer *bubble; @end @implementation RDLabelView -(void)awakeFromNib { self.tapper = [UITapGestureRecognizer new]; [self addGestureRecognizer:self.tapper]; self.bubble = [CAShapeLayer layer]; self.bubble.fillColor = [UIColor colorWithRed:.5 green:1 blue:1 alpha:1].CGColor; [self.layer addSublayer:self.bubble]; self.label = [UILabel new]; self.label.numberOfLines = 0; self.label.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:self.label]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[label]-10-|" options:0 metrics:nil views:@{@"label":self.label}]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[label]-10-|" options:0 metrics:nil views:@{@"label":self.label}]]; } -(void)setColor:(UIColor *)color { // The color property is declared in the .h file to allow color changes to the bubble _color = color; self.bubble.fillColor = _color.CGColor; } -(void)setText:(NSString *)text { // The text property is declared in the .h file to allow you to set the text as you would for a UILabel _text = text; self.label.text = text; } -(UIBezierPath *)appendShapeToRoundedRect:(UIBezierPath *) shape { CGPoint lowerLeftCorner = CGPointMake(0, shape.bounds.size.height); CGPoint pointToStart = CGPointMake(lowerLeftCorner.x + 30, lowerLeftCorner.y); UIBezierPath * arrow = [UIBezierPath bezierPath]; [arrow moveToPoint:pointToStart]; [arrow addLineToPoint:CGPointMake(pointToStart.x, pointToStart.y + 20)]; [arrow addLineToPoint:CGPointMake(pointToStart.x + 10, pointToStart.y)]; [shape appendPath:arrow]; return shape; } -(void)layoutSubviews { [super layoutSubviews]; UIBezierPath *shape = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:16]; shape = [self appendShapeToRoundedRect:shape]; self.bubble.path = shape.CGPath; }