I recently ran into an issue with a custom font I was using with a project. The label on UIButton was cutting off many of the letters at the beginning and end of the label, even though my button was very wide and very tall. After a lot of frustration, I finally determined that I was going to have to make my own button implementation.
You see, Apple (or whoever designed the UIButton class) decided to auto-calculate the width of the button’s label based on the reported width of the font letters. I came to this conclusion after a lot of research and a lot of conjecture. The issue comes up primarily with italic and cursive fonts. These fonts are designed to nest the letters for a nicer effect. The way they nest is that the font designer actually draws the letter intentionally outside of its calculated bounds. This allows two cursive letters to touch and other styles of letters to be closer together so that a letter like ‘f’ doesn’t look abnormally far away from a letter ‘t’.
The font letters designed to be outside of the box is so common that I cannot believe Apple would do such a thing as calculate the width of the label based on the letter width calculations. There is no way for you, the developer, to override this calculation to make that label wider. I won’t lie — I spent 3 days fuming at Apple for making this mess for me. And then I got down to business — subclassing UIButton.
Before:
After: 
In a lot of cases, simply placing a label with a transparent background over your button would be sufficient, and it probably makes more sense to do that if you can get away with it. However, I was moving these buttons around the screen and using the prebuilt button actions like touchUpInside, setHighlighted:, setSelected:, etc and I didn’t want to lose all of that by making a custom UIView that looked like a button. The answer was to subclass UIButton. I searched the internet quite a bit to make sure I was doing it right (I didn’t want to lose any of the code I’d already written that utilized UIButton) and a lot of posts said things like “you can’t subclass UIButton”, “you should make a category”, or plain old made it sound so complicated no one would want to try it.
I did subclass UIButton, it wasn’t that complicated, and it works very well in my application. Here’s the code:
@interface LetterTileButton : UIButton {
UILabel *buttonTitleLabel; //This is the custom label so that I can calculate my own width
UIColor *titleColor; //Remember the color of the label in case I call setHighlighted or setSelected manually
NSString *buttonTitle; //Save the title for later reference, if needed
}
//Manual setter and getter for the font because for UIButton, the font is a property of the title label. You could also override the UIButton methods, but this was better for my situation
-(void)setTitleFont:(UIFont *)font;
-(UIFont *)getTitleFont;
@end
@implementation LetterTileButton
//implement initWithFrame to instantiate your button class from code. Use initWithCoder to instantiate from IB
-(id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
//make the label and format it the way you want, add it as a subview to the button
buttonTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, -5, self.frame.size.width, self.frame.size.height + 10)];
buttonTitleLabel.backgroundColor = [UIColor clearColor];
buttonTitleLabel.textColor = [UIColor blackColor];
buttonTitleLabel.textAlignment = UITextAlignmentCenter;
buttonTitleLabel.clipsToBounds = NO;
[self addSubview:buttonTitleLabel];
}
return self;
}
//
- (void)drawRect:(CGRect)rect
{
buttonTitleLabel.text = buttonTitle;
//update the frame of your label in case the button frame has changed
buttonTitleLabel.frame = CGRectMake(0, -5, self.frame.size.width, self.frame.size.height);
}
//
//override all of the button methods you need and apply the formatting changes to your label
-(void)setTitle:(NSString *)title forState:(UIControlState)state {
buttonTitle = title;
buttonTitleLabel.text = title;
[self setNeedsDisplay];
}
//
-(void)setTitleColor:(UIColor *)color forState:(UIControlState)state {
titleColor = color;
buttonTitleLabel.textColor = color;
[self setNeedsDisplay];
}
//
-(NSString *)titleForState:(UIControlState)state {
return buttonTitleLabel.text;
}
//
-(void)setSelected:(BOOL)selected {
if (selected) {
buttonTitleLabel.textColor = [UIColor lightGrayColor];
}
else {
buttonTitleLabel.textColor = titleColor;
}
[self setNeedsDisplay];
}
//
-(void)setHighlighted:(BOOL)highlighted {
if (highlighted) {
buttonTitleLabel.textColor = [UIColor lightGrayColor];
}
else {
buttonTitleLabel.textColor = titleColor;
}
[self setNeedsDisplay];
}
//
//my custom title font setter and getter
-(void)setTitleFont:(UIFont *)font {
buttonTitleLabel.font = font;
[self setNeedsDisplay];
}
//
-(UIFont *)getTitleFont {
return buttonTitleLabel.font;
}
@end
And to implement the button, you can no longer use buttonWithType:, you have to use the alloc/init method:LetterTileButton *letterTile = [[LetterTileButton alloc] initWithFrame:letterFrame];
And then you can use all of the normal property setters because you overrode them in your subclass. It’s actually not that difficult to subclass UIButton!
[letterTile setTitle:myTitle forState:UIControlStateNormal];[letterTile setFont:myFont]; //this is my custom font setter







