UIButton 上的 iOS NSAttributedString

Posted

技术标签:

【中文标题】UIButton 上的 iOS NSAttributedString【英文标题】:iOS NSAttributedString on UIButton 【发布时间】:2013-07-19 21:50:20 【问题描述】:

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

我想做什么:

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

    第一行全部大写(我意识到这不是属性的一部分) 粗体在第一行 下划线在第一行 第二行显示“正常”重量 第二行没有下划线 以两条线为中心

到目前为止,我已经能够得到 # 的 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.'

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

    多行居中标签 属性标签

如果我必须,我可以接受其中一个或另一个,但我不敢相信我不能拥有一个看似相当简单的概念。

谁能告诉我我做错了什么?

这是我正在尝试的代码的最后一次迭代:

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

【问题讨论】:

如何设置 UILabel 的样式并将其添加到按钮中? titleLabel 不应该是UILabel 吗?我想我可以做到这一点,但我仍然觉得这是一个比应该需要的更大的锤子。 这是 ios,没有任何意义......我的孩子。前段时间我把 NSMutableAttributedString 搞砸了,这不过是头疼。我的建议是让事情尽可能简单易行。 我不反对,但我仍然坚持我的愿望,如上所述,并不完全是“尖端”! ;-) 我不明白为什么我可以用股票UILabel 做到这一点,但它显然在UIButton 上不起作用。奇怪的。看看你的想法是否可行。 @JeffCompton 所以......你的答案很完美!不过,我仍然不喜欢这个解决方案。感觉“笨拙”和错误。我不是在争论它的功效,只是说它不应该是必要的。谢谢您的帮助!如果您将您的评论包装为答案,我会接受它(除非有人给我一种“本机”做我想做的事情的方法)。 【参考方案1】:

在我看来,您好像忘记在代码中使用您设置的“样式”对象......您只是实例化了它。您应该将代码修改为如下所示:

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

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

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

【讨论】:

当我使用属性文本时,有人有解决方案吗? ***.com/questions/24525224/…,我在这里得到了答案。 如何添加颜色? 我知道了NSForegroundColorAttributeName: UIColor.white 如果不使用下划线样式,需要在第1行和第2行之间加一个'\n'字符强制换行。【参考方案2】:

在 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")
    


用法:

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



如果您确实需要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
    


用法:

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



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

【讨论】:

【参考方案3】:

Swift 5.1 中的两行 UIButtonNSAttributedString 标题:

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)

【讨论】:

以上是关于UIButton 上的 iOS NSAttributedString的主要内容,如果未能解决你的问题,请参考以下文章

UIButton 上的 iOS NSAttributedString

iOS - UIPageControl 上的 UIButton 不起作用

具有目标操作的单元格上的 iOS UIButton,单元格未在 (id)sender 中更新

显示自定义 UIButton 上的灰色叠加层

iOS5 上奇怪的 UIButton 行为

iOS·UIButton如何文字在下图片在上