如何更改 Swift 中 UIButton 的字母间距?
How to change letter spacing of UIButton in Swift?
我找到了如何将字母间距设置为 UILabel (here),但此方法不适用于 UIButton。有人知道怎么做吗?
这是我正在使用的代码
let buttonString = agreementButton.attributedTitleForState(.Normal) as! NSMutableAttributedString
buttonString.addAttribute(NSKernAttributeName, value: 1.0, range: NSMakeRange(0, buttonString.length))
agreementButton.setAttributedTitle(buttonString, forState: .Normal)
它抛出一个错误:'NSConcreteAttributedString' (0x19e508660) to 'NSMutableAttributedString' (0x19e506a40).
- 将
NSAttributedString
设为您链接的问题中的
- 在您的
UIButton
上致电 setAttributedTitle(_ ,forState:)
试试这个(未测试):
let title = agreementButton.titleForState(.Normal)
let attributedTitle = NSAttributedString(string: title, attributes: [NSKernAttributeName: 1.0])
agreementButton.setAttributedTitle(attributedTitle, forState: .Normal)
Code Different 中的解决方案不考虑文本颜色设置。也可以重写 UIButton class 以使间距参数即使在情节提要中也可用。这是一个更新的 Swift 3 解决方案:
Swift 3
class UIButtonWithSpacing : UIButton
{
override func setTitle(_ title: String?, for state: UIControlState)
{
if let title = title, spacing != 0
{
let color = super.titleColor(for: state) ?? UIColor.black
let attributedTitle = NSAttributedString(
string: title,
attributes: [NSKernAttributeName: spacing,
NSForegroundColorAttributeName: color])
super.setAttributedTitle(attributedTitle, for: state)
}
else
{
super.setTitle(title, for: state)
}
}
fileprivate func updateTitleLabel_()
{
let states:[UIControlState] = [.normal, .highlighted, .selected, .disabled]
for state in states
{
let currentText = super.title(for: state)
self.setTitle(currentText, for: state)
}
}
@IBInspectable var spacing:CGFloat = 0 {
didSet {
updateTitleLabel_()
}
}
}
Swift 3.0
extension UIButton{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.characters.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
btnRegister.addTextSpacing(spacing: 4.5)
extension UILabel{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.text!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.text!.characters.count))
self.attributedText = attributedString
}
}
lblOne.addTextSpacing(spacing: 4.5)
extension UITextField{
func addPlaceholderSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.placeholder!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.placeholder!.characters.count))
self.attributedPlaceholder = attributedString
}
}
txtUserName.addPlaceholderSpacing(spacing: 4.5)
extension UINavigationItem{
func addSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.title!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.title!.characters.count))
let label = UILabel()
label.textColor = UIColor.black
label.font = UIFont.systemFont(ofSize: 15, weight: UIFontWeightBold)
label.attributedText = attributedString
label.sizeToFit()
self.titleView = label
}
}
navigationItem.addSpacing(spacing: 2.5)
Swift 4
extension UIButton{
func addTextSpacing(_ letterSpacing: CGFloat){
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSAttributedString.Key.kern, value: letterSpacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
// Usage: button.addTextSpacing(5.0)
不是完整答案,而是一个 GOTCHA 和 FIX。
问题所在:应用字符间距还会将 space 添加到文本的末尾。这 misfeature/bug 意味着居中对齐的文本将显示不正确。
FIX:为属性文本创建范围时,从 text.count 值中减去 1(因此忽略字符串中的最后一个字符以用于间距目的。)
例如由于额外 space:
而导致不正确的居中
固定:
[已编辑]
相关说明,如果您使用 EdgeInsets 在文本周围添加填充,您的 UIButton
子类将需要覆盖 intrinsicContentsSize:
override open var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
let insets = self.titleEdgeInsets
let width = size.width + insets.left + insets.right
let height = size.height + insets.top + insets.bottom
return CGSize(width: width, height: height)
}
更新 Swift 4 基于 jaya raj
的回答。
extension UIButton{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.characters.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
extension UILabel{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.text!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: self.text!.characters.count))
self.attributedText = attributedString
}
}
extension UITextField{
func addPlaceholderSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.placeholder!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: self.placeholder!.characters.count))
self.attributedPlaceholder = attributedString
}
}
extension UINavigationItem{
func addSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.title!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: self.title!.characters.count))
let label = UILabel()
label.textColor = UIColor.black
label.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.bold)
label.attributedText = attributedString
label.sizeToFit()
self.titleView = label
}
}
Swift 5 的更新,无需强制解包:
我觉得这是一个更好的解决方案,因为我们也可以满足按钮上的现有属性
func addTextSpacing(_ letterSpacing: CGFloat) {
let attributedString = attributedTitle(for: .normal)?.mutableCopy() as? NSMutableAttributedString ??
NSMutableAttributedString(string: title(for: .normal) ?? "")
attributedString.addAttribute(NSAttributedString.Key.kern, value: letterSpacing,
range: NSRange(location: 0, length: attributedString.string.count))
self.setAttributedTitle(attributedString, for: .normal)
}
Swift 5 扩展在这里。
extension UIButton {
@IBInspectable
var letterSpacing: CGFloat {
set {
let attributedString: NSMutableAttributedString
if let currentAttrString = attributedTitle(for: .normal) {
attributedString = NSMutableAttributedString(attributedString: currentAttrString)
}
else {
attributedString = NSMutableAttributedString(string: self.title(for: .normal) ?? "")
setTitle(.none, for: .normal)
}
attributedString.addAttribute(NSAttributedString.Key.kern, value: newValue, range: NSRange(location: 0, length: attributedString.length))
setAttributedTitle(attributedString, for: .normal)
}
get {
if let currentLetterSpace = attributedTitle(for: .normal)?.attribute(NSAttributedString.Key.kern, at: 0, effectiveRange: .none) as? CGFloat {
return currentLetterSpace
}
else {
return 0
}
}
}
}
用法:您可以在故事板或代码中设置字母space。
button.letterSpacing = 2.0
Swift 5
guard let buttonTitle = button.title(for: .normal) else { return }
let attributedTitle = NSAttributedString(string: buttonTitle, attributes: [NSAttributedString.Key.kern: kernValue])
button.setAttributedTitle(attributedTitle, for: .normal)
我找到了如何将字母间距设置为 UILabel (here),但此方法不适用于 UIButton。有人知道怎么做吗?
这是我正在使用的代码
let buttonString = agreementButton.attributedTitleForState(.Normal) as! NSMutableAttributedString
buttonString.addAttribute(NSKernAttributeName, value: 1.0, range: NSMakeRange(0, buttonString.length))
agreementButton.setAttributedTitle(buttonString, forState: .Normal)
它抛出一个错误:'NSConcreteAttributedString' (0x19e508660) to 'NSMutableAttributedString' (0x19e506a40).
- 将
NSAttributedString
设为您链接的问题中的 - 在您的
UIButton
上致电
setAttributedTitle(_ ,forState:)
试试这个(未测试):
let title = agreementButton.titleForState(.Normal)
let attributedTitle = NSAttributedString(string: title, attributes: [NSKernAttributeName: 1.0])
agreementButton.setAttributedTitle(attributedTitle, forState: .Normal)
Code Different 中的解决方案不考虑文本颜色设置。也可以重写 UIButton class 以使间距参数即使在情节提要中也可用。这是一个更新的 Swift 3 解决方案:
Swift 3
class UIButtonWithSpacing : UIButton
{
override func setTitle(_ title: String?, for state: UIControlState)
{
if let title = title, spacing != 0
{
let color = super.titleColor(for: state) ?? UIColor.black
let attributedTitle = NSAttributedString(
string: title,
attributes: [NSKernAttributeName: spacing,
NSForegroundColorAttributeName: color])
super.setAttributedTitle(attributedTitle, for: state)
}
else
{
super.setTitle(title, for: state)
}
}
fileprivate func updateTitleLabel_()
{
let states:[UIControlState] = [.normal, .highlighted, .selected, .disabled]
for state in states
{
let currentText = super.title(for: state)
self.setTitle(currentText, for: state)
}
}
@IBInspectable var spacing:CGFloat = 0 {
didSet {
updateTitleLabel_()
}
}
}
Swift 3.0
extension UIButton{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.characters.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
btnRegister.addTextSpacing(spacing: 4.5)
extension UILabel{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.text!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.text!.characters.count))
self.attributedText = attributedString
}
}
lblOne.addTextSpacing(spacing: 4.5)
extension UITextField{
func addPlaceholderSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.placeholder!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.placeholder!.characters.count))
self.attributedPlaceholder = attributedString
}
}
txtUserName.addPlaceholderSpacing(spacing: 4.5)
extension UINavigationItem{
func addSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.title!)
attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.title!.characters.count))
let label = UILabel()
label.textColor = UIColor.black
label.font = UIFont.systemFont(ofSize: 15, weight: UIFontWeightBold)
label.attributedText = attributedString
label.sizeToFit()
self.titleView = label
}
}
navigationItem.addSpacing(spacing: 2.5)
Swift 4
extension UIButton{
func addTextSpacing(_ letterSpacing: CGFloat){
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSAttributedString.Key.kern, value: letterSpacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
// Usage: button.addTextSpacing(5.0)
不是完整答案,而是一个 GOTCHA 和 FIX。
问题所在:应用字符间距还会将 space 添加到文本的末尾。这 misfeature/bug 意味着居中对齐的文本将显示不正确。
FIX:为属性文本创建范围时,从 text.count 值中减去 1(因此忽略字符串中的最后一个字符以用于间距目的。)
例如由于额外 space:
而导致不正确的居中固定:
[已编辑]
相关说明,如果您使用 EdgeInsets 在文本周围添加填充,您的 UIButton
子类将需要覆盖 intrinsicContentsSize:
override open var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
let insets = self.titleEdgeInsets
let width = size.width + insets.left + insets.right
let height = size.height + insets.top + insets.bottom
return CGSize(width: width, height: height)
}
更新 Swift 4 基于 jaya raj
的回答。
extension UIButton{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.characters.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
extension UILabel{
func addTextSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.text!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: self.text!.characters.count))
self.attributedText = attributedString
}
}
extension UITextField{
func addPlaceholderSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.placeholder!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: self.placeholder!.characters.count))
self.attributedPlaceholder = attributedString
}
}
extension UINavigationItem{
func addSpacing(spacing: CGFloat){
let attributedString = NSMutableAttributedString(string: self.title!)
attributedString.addAttribute(NSAttributedStringKey.kern, value: spacing, range: NSRange(location: 0, length: self.title!.characters.count))
let label = UILabel()
label.textColor = UIColor.black
label.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.bold)
label.attributedText = attributedString
label.sizeToFit()
self.titleView = label
}
}
Swift 5 的更新,无需强制解包:
我觉得这是一个更好的解决方案,因为我们也可以满足按钮上的现有属性
func addTextSpacing(_ letterSpacing: CGFloat) {
let attributedString = attributedTitle(for: .normal)?.mutableCopy() as? NSMutableAttributedString ??
NSMutableAttributedString(string: title(for: .normal) ?? "")
attributedString.addAttribute(NSAttributedString.Key.kern, value: letterSpacing,
range: NSRange(location: 0, length: attributedString.string.count))
self.setAttributedTitle(attributedString, for: .normal)
}
Swift 5 扩展在这里。
extension UIButton {
@IBInspectable
var letterSpacing: CGFloat {
set {
let attributedString: NSMutableAttributedString
if let currentAttrString = attributedTitle(for: .normal) {
attributedString = NSMutableAttributedString(attributedString: currentAttrString)
}
else {
attributedString = NSMutableAttributedString(string: self.title(for: .normal) ?? "")
setTitle(.none, for: .normal)
}
attributedString.addAttribute(NSAttributedString.Key.kern, value: newValue, range: NSRange(location: 0, length: attributedString.length))
setAttributedTitle(attributedString, for: .normal)
}
get {
if let currentLetterSpace = attributedTitle(for: .normal)?.attribute(NSAttributedString.Key.kern, at: 0, effectiveRange: .none) as? CGFloat {
return currentLetterSpace
}
else {
return 0
}
}
}
}
用法:您可以在故事板或代码中设置字母space。
button.letterSpacing = 2.0
Swift 5
guard let buttonTitle = button.title(for: .normal) else { return }
let attributedTitle = NSAttributedString(string: buttonTitle, attributes: [NSAttributedString.Key.kern: kernValue])
button.setAttributedTitle(attributedTitle, for: .normal)