IOS NSAttributedString on UIButton

I use iOS 6 , so attribute strings should be easy to use, right? Well ... not so much.

What I want to do:

Using a custom subclass of UIButton (it doesn't do anything custom for titleLabel ), I would like to have a multi-line related header that:

  • All caps (I understand that not part of the attributes) in the first line
  • Bold in the first line
  • Underlined in the first line
  • "Normal" weight in the second row
  • No underline on the second line
  • Centered on both lines

I was able to get No. 1 to 5 (at least I thought I did, but the current testing leads to multi-line text errors), but when I tried to do something (anything!) So that the text was centered, mine The application continues to crash. When I try to get all 6 elements working (using various methods), I get the following crash / error:

 Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSAttributedString invalid for autoresizing, it must have a single spanning paragraph style (or none) with a non-wrapping lineBreakMode.' 

Based on what I tried, it seems that I can have one of the following options, but not both:

  • Multi-line, centered label
  • Attributed Label

I can live with one or the other if I have to , but I cannot believe that I cannot have what seems like a pretty simple concept.

Can someone please tell me what I'm wrong about?

Here's the last iteration of the code I'm trying:

 NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; [style setAlignment:NSTextAlignmentCenter]; [style setLineBreakMode:NSLineBreakByWordWrapping]; UIFont *font1 = [UIFont fontWithName:@"HelveticaNeue-Medium" size:20.0f]; UIFont *font2 = [UIFont fontWithName:@"HelveticaNeue-Light" size:20.0f]; NSDictionary *dict1 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle), NSFontAttributeName:font1}; NSDictionary *dict2 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleNone), NSFontAttributeName:font2}; NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] init]; [attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"LINE 1\n" attributes:dict1]]; [attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"line 2" attributes:dict2]]; [[self buttonToStyle] setAttributedTitle:attString forState:UIControlStateNormal]; [[[self buttonToStyle] titleLabel] setNumberOfLines:0]; [[[self buttonToStyle] titleLabel] setLineBreakMode:NSLineBreakByWordWrapping]; 
+55
ios uibutton nsattributedstring
Jul 19 '13 at 21:50
source share
2 answers

It seems to me that you forgot to use the "style" object that you created in your code .. you just created it. You should change your code like this:

 NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; [style setAlignment:NSTextAlignmentCenter]; [style setLineBreakMode:NSLineBreakByWordWrapping]; UIFont *font1 = [UIFont fontWithName:@"HelveticaNeue-Medium" size:20.0f]; UIFont *font2 = [UIFont fontWithName:@"HelveticaNeue-Light" size:20.0f]; NSDictionary *dict1 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle), NSFontAttributeName:font1, NSParagraphStyleAttributeName:style}; // Added line NSDictionary *dict2 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleNone), NSFontAttributeName:font2, NSParagraphStyleAttributeName:style}; // Added line NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] init]; [attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"LINE 1\n" attributes:dict1]]; [attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"line 2" attributes:dict2]]; [self.resolveButton setAttributedTitle:attString forState:UIControlStateNormal]; [[self.resolveButton titleLabel] setNumberOfLines:0]; [[self.resolveButton titleLabel] setLineBreakMode:NSLineBreakByWordWrapping]; 

Note that I only added lines that define the name NSParagraphStyleAttributeName .. everything else is the same .. and this is what I get for the button:

enter image description here

And here he is in Swift 3.0

 let style = NSMutableParagraphStyle() style.alignment = .center style.lineBreakMode = .byWordWrapping guard let font1 = UIFont(name: "HelveticaNeue-Medium", size: 20), let font2 = UIFont(name: "HelveticaNeue-Light", size: 20) else { return } let dict1:[String:Any] = [ NSUnderlineStyleAttributeName:NSUnderlineStyle.styleSingle.rawValue, NSFontAttributeName:font1, NSParagraphStyleAttributeName:style ] let dict2:[String:Any] = [ NSUnderlineStyleAttributeName:NSUnderlineStyle.styleNone.rawValue, NSFontAttributeName:font2, NSParagraphStyleAttributeName:style ] let attString = NSMutableAttributedString() attString.append(NSAttributedString(string: "LINE 1", attributes: dict1)) attString.append(NSAttributedString(string: "line 2", attributes: dict2)) button.setAttributedTitle(attString, for: .normal) button.titleLabel?.numberOfLines = 0 button.titleLabel?.lineBreakMode = .byWordWrapping 
+93
Mar 13 '14 at 15:41
source share

With Swift 4, you can use the implementation of the UIButton subclass below to solve your problem:

 import UIKit class CustomButton: UIButton { required init(title: String, subtitle: String) { super.init(frame: CGRect.zero) let style = NSMutableParagraphStyle() style.alignment = NSTextAlignment.center style.lineBreakMode = NSLineBreakMode.byWordWrapping let titleAttributes: [NSAttributedStringKey : Any] = [ NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue, NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.largeTitle), NSAttributedStringKey.paragraphStyle : style ] let subtitleAttributes = [ NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.body), NSAttributedStringKey.paragraphStyle : style ] let attributedString = NSMutableAttributedString(string: title, attributes: titleAttributes) attributedString.append(NSAttributedString(string: "\n")) attributedString.append(NSAttributedString(string: subtitle, attributes: subtitleAttributes)) setAttributedTitle(attributedString, for: UIControlState.normal) titleLabel?.numberOfLines = 0 titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 

Using:

 import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let button = CustomButton(title: "Title", subtitle: "Subtitle") button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor) let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor) NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint]) } } 



Alternatively, if you really need a button like system , you can use the following code:

 import UIKit extension UIButton { static func customSystemButton(title: String, subtitle: String) -> UIButton { let style = NSMutableParagraphStyle() style.alignment = NSTextAlignment.center style.lineBreakMode = NSLineBreakMode.byWordWrapping let titleAttributes: [NSAttributedStringKey : Any] = [ NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue, NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.largeTitle), NSAttributedStringKey.paragraphStyle : style ] let subtitleAttributes = [ NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.body), NSAttributedStringKey.paragraphStyle : style ] let attributedString = NSMutableAttributedString(string: title, attributes: titleAttributes) attributedString.append(NSAttributedString(string: "\n")) attributedString.append(NSAttributedString(string: subtitle, attributes: subtitleAttributes)) let button = UIButton(type: UIButtonType.system) button.setAttributedTitle(attributedString, for: UIControlState.normal) button.titleLabel?.numberOfLines = 0 button.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping return button } } 

Using:

 import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let button = UIButton.customSystemButton(title: "Title", subtitle: "Subtitle") button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor) let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor) NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint]) } } 



The two screenshots below show the results for a subclass of UIButton (left) and a button of type system (right):

enter image description here

+6
Aug 02 '17 at 11:00
source share



All Articles