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

时间:2015-04-06 23:19:08

标签: ios objective-c cocoa-touch uibutton

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

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

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

- (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)]];

}

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

screenshot

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

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

2 个答案:

答案 0 :(得分:3)

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

  • 制作标签包装,并根据其内容的大小垂直增长或缩小。这很简单,是以前解决的问题。

  • 使标签的背景清晰。

  • 将图像视图限制在与其后面的标签相同的位置和大小。

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

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

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

enter image description here

enter image description here

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

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

答案 1 :(得分:1)

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

#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;
}