UIButton 上的 iOS NSAttributedString

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17756067/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:25:04  来源:igfitidea点击:

iOS NSAttributedString on UIButton

iosuibuttonnsattributedstring

提问by mbm29414

I'm using iOS 6, so attributed strings should be easy to use, right? Well... not so much.

我使用的是iOS 6,所以属性字符串应该很容易使用,对吧?嗯……没那么多。

What I want to do:

我想做的事:

Using a custom subclass of UIButton(it doesn't do anything custom to titleLabel), I'd like to have a multi-line, attributed title that is:

使用的自定义子类UIButton(它不做任何自定义titleLabel),我想要一个多行的属性标题,即:

  1. All caps (I realize that's not part of the attributes) on the first line
  2. Boldedon the first line
  3. Underlinedon the first line
  4. "Normal" weight on the second line
  5. Non-underlined on the second line
  6. Centered on both lines
  1. 第一行全部大写(我意识到这不是属性的一部分)
  2. 在第一行加粗
  3. 第一行下划线
  4. 第二行的“正常”权重
  5. 第二行无下划线
  6. 以两条线为中心

I've been able to get #'s 1 through 5 so far (at least, I thought I did, but current testing is yielding errors with multi-line text), but when I tried to do something (anything!) to get the text to be centered, my app keeps crashing. When I try to get all 6 items working (through various methods), I get the following crash/error:

到目前为止,我已经能够获得 # 的 1 到 5(至少,我认为我做到了,但是当前的测试会产生多行文本的错误),但是当我尝试做某事(任何事情!)文本居中,我的应用程序不断崩溃。当我尝试让所有 6 个项目都工作(通过各种方法)时,我收到以下崩溃/错误:

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've tried, it appears that I can have one of the following options, but not both:

根据我的尝试,我似乎可以有以下选择之一,但不能同时拥有:

  1. A multi-line, centered label
  2. An attributed label
  1. 多行居中标签
  2. 属性标签

I can live with one or the other if I must, but I can't believe that I can't have what seems to be a fairly straightforward concept.

如果必须的话,我可以与其中一个一起生活,但我无法相信我无法拥有一个看起来相当简单的概念。

Can someone please tell me what I've got wrong?

有人可以告诉我我有什么问题吗?

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];

回答by ccwasden

It looks to me like you forgot in your code to use the "style" object that you set up.. you just instantiated it. You should modify your code to look 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 the lines that define the NSParagraphStyleAttributeName.. everything else is the same.. and this is what I get for the button:

请注意,我只添加了定义 NSParagraphStyleAttributeName 的行.. 其他一切都一样.. 这就是我得到的按钮:

enter image description here

在此处输入图片说明

And here it is in Swift 3.0

这是在 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

回答by Imanou Petit

With Swift 5.1 and iOS 13.1, you can use the UIButtonsubclass implementation below in order to solve your problem:

使用 Swift 5.1 和 iOS 13.1,您可以使用UIButton下面的子类实现来解决您的问题:

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: [NSAttributedString.Key : Any] = [
            NSAttributedString.Key.foregroundColor: UIColor.label,
            NSAttributedString.Key.underlineStyle : NSUnderlineStyle.single.rawValue,
            NSAttributedString.Key.font : UIFont.preferredFont(forTextStyle: UIFont.TextStyle.largeTitle),
            NSAttributedString.Key.paragraphStyle : style
        ]
        let subtitleAttributes = [
            NSAttributedString.Key.foregroundColor: UIColor.label,
            NSAttributedString.Key.font : UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body),
            NSAttributedString.Key.paragraphStyle : style
        ]

        let attributedString = NSMutableAttributedString(string: title, attributes: titleAttributes)
        attributedString.append(NSAttributedString(string: "\n"))
        attributedString.append(NSAttributedString(string: subtitle, attributes: subtitleAttributes))

        setAttributedTitle(attributedString, for: UIControl.State.normal)
        titleLabel?.numberOfLines = 0
        titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

Usage:

用法:

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])
    }

}


As an alternative if you really need a button of type system, you may use the following code:

作为替代,如果您确实需要 type 按钮system,则可以使用以下代码:

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: [NSAttributedString.Key : Any] = [
            NSAttributedString.Key.underlineStyle : NSUnderlineStyle.single.rawValue,
            NSAttributedString.Key.font : UIFont.preferredFont(forTextStyle: UIFont.TextStyle.largeTitle),
            NSAttributedString.Key.paragraphStyle : style
        ]
        let subtitleAttributes = [
            NSAttributedString.Key.font : UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body),
            NSAttributedString.Key.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: UIButton.ButtonType.system)
        button.setAttributedTitle(attributedString, for: UIControl.State.normal)
        button.titleLabel?.numberOfLines = 0
        button.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping

        return button
    }

}

Usage:

用法:

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 screen shots below show the result display for the UIButtonsubclass (on the left) and for the systemtype button (on the right):

下面的两个屏幕截图显示了UIButton子类(左侧)和system类型按钮(右侧)的结果显示:

enter image description here

在此处输入图片说明

回答by Denis Kutlubaev

Two line UIButtonwith NSAttributedStringtitle in Swift 5.1:

两线UIButtonNSAttributedString在标题Swift 5.1

func customizeSubscribeButton() {
    subscribeButton.titleLabel?.numberOfLines = 2
    let title = NSMutableAttributedString()
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center
    let part1 = NSAttributedString(string: "SUBSCRIBE FOR 12 MONTH\n",
                                   attributes: [NSAttributedString.Key.foregroundColor: UIColor.white,
                                                NSAttributedString.Key.font: UIFont.systemFont(ofSize: 24, weight: .semibold),
                                                NSAttributedString.Key.paragraphStyle: paragraphStyle])
    let part2 = NSAttributedString(string: "999.00 RUB ECONOMY 85%",
                                   attributes: [NSAttributedString.Key.foregroundColor: UIColor.white,
                                                NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .light),
                                                NSAttributedString.Key.paragraphStyle: paragraphStyle])
    title.append(part1)
    title.append(part2)
    subscribeButton.setAttributedTitle(title, for: .normal)
}