Talking with Benoit Cerrina (of ShinKanji) about a related issue, I started to wonder how you could render “bubble” text, as for instance in the SMS application. It turns out to be pretty easy; all you need is a little source art, and some tuning parameters.
Prerendered
At first, I was looking at this problem in terms of the Shiny Red Button work I’d done earlier, but came to feel that this was misguided; SMS bubbles are basically stretched images, with fixed-height top and bottom caps, and fixed-width left and right caps, so they’re much better candidates for pre-rendering than the buttons we’ve seen before. (Also, their internal graphical structure looks a little more finicky.)
Accordingly, I pulled together the source image seen to the left by reverse-engineering a screenshot of the SMS application. It’s 30×42, with a 13-pixel top cap and a 24-pixel left cap. (This implies a 16-pixel bottom cap, a 17-pixel right cap, and that the 14th row and 25th column are stretched as necessary.)
Implementation
With this art stored safely in a file, we can use the following code to produce UIViews
containing “bubble” text:
- (UIView*)makeBubbleWithWidth:(CGFloat)w font:(UIFont*)f text:(NSString*)s background:(NSString*)fn caps:(CGSize)caps padding:(CGFloat*)padTRBL
{
// Create label
UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, w, 1)];
// Configure (for multi-line word-wrapping)
label.font = f;
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
// Set and size
label.text = s;
[label sizeToFit];
// Size and create final view
CGSize finalSize = CGSizeMake(label.frame.size.width+padTRBL[1]+padTRBL[3], label.frame.size.height+padTRBL[0]+padTRBL[2]);
UIView* finalView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, finalSize.width, finalSize.height)];
// Create stretchable BG image
UIImage* bubble = [[UIImage imageNamed:fn] stretchableImageWithLeftCapWidth:caps.width topCapHeight:caps.height];
UIImageView* background = [[UIImageView alloc] initWithImage:bubble];
background.frame = finalView.frame;
// Assemble composite (with padding for label)
[finalView addSubview:background];
[finalView addSubview:label];
label.backgroundColor = [UIColor clearColor];
label.frame = CGRectMake(padTRBL[3], padTRBL[0], label.frame.size.width, label.frame.size.height);
// Clean and return
[label release];
[background release];
return [finalView autorelease];
}
You can drive this code from a loadView
such as the following, which produced the screenshot at the top of this article:
- (void)loadView
{
// TL view
self.view = [[UIView alloc] initWithFrame:CGRectZero];
self.view.backgroundColor = [UIColor colorWithRed:219/255.0 green:226/255.0 blue:237/255.0 alpha:1];
// Settings
NSString* s;
NSString* art = @"bubble.png";
CGSize caps = CGSizeMake(24, 13);
UIFont* font = [UIFont systemFontOfSize:16];
CGFloat padTRBL[4] = {4, 16, 7, 22};
UIView* bubble;
// Create bubble
s = @"Here's a little bubble";
bubble = [self makeBubbleWithWidth:220 font:font text:s background:art caps:caps padding:padTRBL];
bubble.frame = CGRectMake(0, 10, bubble.frame.size.width, bubble.frame.size.height);
[self.view addSubview:bubble];
s = @"Here's a longer one, away from the margins";
bubble = [self makeBubbleWithWidth:80 font:font text:s background:art caps:caps padding:padTRBL];
bubble.frame = CGRectMake(200, 20, bubble.frame.size.width, bubble.frame.size.height);
[self.view addSubview:bubble];
s = @"Bubbles respect\nnewlines\nin\nthe\nsource text ...";
bubble = [self makeBubbleWithWidth:180 font:font text:s background:art caps:caps padding:padTRBL];
bubble.frame = CGRectMake(0, 60, bubble.frame.size.width, bubble.frame.size.height);
[self.view addSubview:bubble];
s = @"Andnormallyaddlinebreaksonlyinbetween words, unless a really long word forces them to do otherwise";
bubble = [self makeBubbleWithWidth:280 font:font text:s background:art caps:caps padding:padTRBL];
bubble.frame = CGRectMake(0, 200, bubble.frame.size.width, bubble.frame.size.height);
[self.view addSubview:bubble];
}
Cheats
The one huge cheat in this code is that the bubble art is pre-blended with a particular background color (R:219/G:226/B:237); there’s no translucency or alpha-blending here, and if these bubbles were laid over one another, or over a more interesting background, they wouldn’t look right. That problem seems pretty fixable, though.
Also, the cap and padding parameters are pretty implementation-specific: The caps depend upon the source art, and the padding depends upon almost everything (source art, UILabel
settings, font, &c).
Pingback: links for 2009-09-19 | manicwave.com
Pingback: Membuat Bubble Chat di Aplikasi iPhone « Heru Prasetia B'loGs