Skip to content

Instantly share code, notes, and snippets.

@jpmhouston
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpmhouston/35686fc811b1c62effe5 to your computer and use it in GitHub Desktop.
Save jpmhouston/35686fc811b1c62effe5 to your computer and use it in GitHub Desktop.
Shrink UITextField placeholder to fit
// call from a view controller's -viewDidLayoutSubviews or -viewWillAppear:
// pass it a text field, etc, assign result to that text field's attributedPlaceholder property
// note that the maxKerning parameter must be a negative value (a "maximally negative" kerning value)
// pro tip: i've sometimes found that a text field still has incorrect bounds in -viewDidLayoutSubviews
// and needs to forcibly have its layout done:
//- (void)viewDidLayoutSubviews {
// [self.field layoutIfNeeded];
// self.field.attributedPlaceholder = [self resizedPlaceholderForField:self.field withPadding:4.0 maxKerningReduction:0.3];
//}
- (NSAttributedString *)resizedPlaceholderForField:(UITextField *)field withPadding:(CGFloat)extraPadding maxKerningReduction:(double)maxKerning
{
NSAttributedString *placeholder = field.attributedPlaceholder;
UIFont *font = [placeholder attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL];
if (!font) {
return placeholder;
}
CGFloat fontSize = font.pointSize;
NSMutableAttributedString *mutablePlaceholder = placeholder.mutableCopy;
CGSize boundsSize = [field placeholderRectForBounds:field.bounds].size;
CGSize unlimitedSize = (CGSize){ FLT_MAX, boundsSize.height };
CGFloat maxWidth = boundsSize.width - extraPadding - extraPadding;
CGFloat fontDecrement = 0.5;
CGFloat kernDecrement = 0.05;
// if doesn't fit, first tighten the letter spacing by increments of .05
BOOL fits = NO;
double kern = 0; // goes down from 0, since loop condition being 'kern > maxKerning', will skip this loop is maxKerning >= 0
while (!fits && kern > maxKerning) {
CGRect measuredRect = [mutablePlaceholder boundingRectWithSize:unlimitedSize options:0 context:nil];
if (!(fits = measuredRect.size.width <= maxWidth)) {
kern -= kernDecrement;
[mutablePlaceholder addAttribute:NSKernAttributeName value:@(kern) range:(NSRange){0, mutablePlaceholder.string.length}];
}
}
// as long as it still doesn't fit, reduce the text size by .5 and try again, down to its minimum
while (!fits && fontSize >= field.minimumFontSize) {
CGRect measuredRect = [mutablePlaceholder boundingRectWithSize:unlimitedSize options:0 context:nil];
if (!(fits = measuredRect.size.width <= maxWidth)) {
fontSize -= fontDecrement;
font = [font fontWithSize:fontSize];
[mutablePlaceholder addAttribute:NSFontAttributeName value:font range:(NSRange){0, mutablePlaceholder.string.length}];
}
}
return mutablePlaceholder;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment