简体   繁体   中英

Creating UIButton attributed title based on its original plain text state

I'm trying to create a subclass of UIButton that enforces text kerning. As such, when I build a button in Interface Builder, set the text colour (using plain text) I'm expecting to be able to take the existing text, colour, paragraph style, and font from the plain titleLabel instance and translate it into an attributed label with the same properties.

I written two categories that I thought might help:

+ (NSMutableAttributedString*)attributedStringWithTitle:(NSString*)title fromExistingAttributedString:(NSAttributedString*)attributedString
{
    NSDictionary *attributes = [attributedString attributesAtIndex:0 effectiveRange:NULL];
    return [[NSMutableAttributedString alloc] initWithString:title attributes:attributes];
}

+ (NSMutableAttributedString*)attributedStringWithTitle:(NSString*)title fromPlainTextLabel:(UILabel*)label
{
    NSMutableAttributedString* mutableTitle = [[NSMutableAttributedString alloc] initWithString:title];
    [mutableTitle addAttribute:NSFontAttributeName value:label.font range:[mutableTitle fullRange]];
    [mutableTitle addAttribute:NSForegroundColorAttributeName value:label.textColor range:[mutableTitle fullRange]];
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    style.alignment = label.textAlignment;
    [mutableTitle addAttribute:NSParagraphStyleAttributeName value:style range:[mutableTitle fullRange]];
    return mutableTitle;
}

And then in my UIButton subclass, I'm overriding these like so:

- (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state
{
    NSMutableAttributedString* mutableTitle = [title mutableCopy];
    [mutableTitle addAttributes:@{NSKernAttributeName: @(kDefaultKerning)} range:[mutableTitle fullRange]];
    [super setAttributedTitle:mutableTitle forState:state];
    [self setNeedsDisplay];
}

- (void)setTitle:(NSString *)title forState:(UIControlState)state
{
    NSMutableAttributedString* mutableTitle;
    if ([self attributedTitleForState:state]) {
        mutableTitle = [NSMutableAttributedString attributedStringWithTitle:title fromExistingAttributedString:[self attributedTitleForState:state]];
    } else {
        mutableTitle = [NSMutableAttributedString attributedStringWithTitle:title fromPlainTextLabel:self.titleLabel];
    }
    [self setAttributedTitle:mutableTitle forState:state];    
}

But something's not working - particularly of note, [self attributedTitleForState:state] and [self titleForState:state] always seem to be nil. Seems to me like there's some disconnect in the way the properties are set in Interface Builder, in that while the text comes through OK, all of the styling is missing from it.

Got this to work finally. Turns out that there are several properties of a plain text UIButton that are in several places. Some have helper methods to retrieve directly from the UIButton, some do not.

  • Font: self.titleLabel.font (no helper method)
  • Colour: [self titleColorForState:state]
  • Text: [self titleForState:state]
  • Attributed Text: [self attributedTitleForState]

And a plain text UIButton does not have any text alignment by default, since that's controlled by content horizontal alignment .

As such, I changed up my helper methods (the first is identical, but I had to change the second):

+ (NSMutableAttributedString*)attributedStringWithTitle:(NSString*)title fromExistingAttributedString:(NSAttributedString*)attributedString
{
    NSDictionary *attributes = [attributedString attributesAtIndex:0 effectiveRange:NULL];
    return [[NSMutableAttributedString alloc] initWithString:title attributes:attributes];
}

+ (NSMutableAttributedString*)attributedStringWithTitle:(NSString*)title font:(UIFont*)font color:(UIColor*)color 
{
    NSMutableAttributedString* mutableTitle = [[NSMutableAttributedString alloc] initWithString:title];
    [mutableTitle addAttribute:NSFontAttributeName value:font range:[mutableTitle fullRange]];
    [mutableTitle addAttribute:NSForegroundColorAttributeName value:color range:[mutableTitle fullRange]];
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    [mutableTitle addAttribute:NSParagraphStyleAttributeName value:style range:[mutableTitle fullRange]];
    return mutableTitle;
}

And I've implemented it like this:

- (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state
{
    NSMutableAttributedString* mutableTitle = [title mutableCopy];
    [mutableTitle addAttributes:@{NSKernAttributeName: @(kDefaultKerning)} range:[mutableTitle fullRange]];
    [super setAttributedTitle:mutableTitle forState:state];
    [self setNeedsDisplay];
}

- (void)setTitle:(NSString *)title forState:(UIControlState)state
{
    NSMutableAttributedString* mutableTitle;
    if ([self attributedTitleForState:state]) {
        mutableTitle = [NSMutableAttributedString attributedStringWithTitle:title fromExistingAttributedString:[self attributedTitleForState:state]];
    } else {
        mutableTitle = [NSMutableAttributedString attributedStringWithTitle:title font:self.titleLabel.font color:[self titleColorForState:state]];
    }
    [self setAttributedTitle:mutableTitle forState:state];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM