自适应titleLabel大小的UIButton

42

我有一个UIButton,我将其添加到故事板中的视图控制器的视图中。我添加居中约束来定位它,并添加前导空间约束来限制其宽度。在代码中,我添加:

self.button.titleLabel.numberOfLines = 0;
self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.button setTitle:@"A real real real real real real real real long long name." forState:UIControlStateNormal];
self.button.backgroundColor = [UIColor redColor];
self.button.titleLabel.backgroundColor = [UIColor blueColor];
下面显示结果: enter image description here 我希望按钮根据其内容大小调整大小。我该如何做?
我已经尝试过
[self.button sizeToFit];

我已经尝试将内容抱紧和压缩阻力自动布局约束的优先级设置为必需。

我还尝试了显式地设置 contentEdgeInsetstitleEdgeInsetsUIEdgeInsetsZero 并调用 invalidateIntrinsicContentSize

我还注意到,如果在标题字符串中放置换行符,则按钮似乎会调整大小以适应其内容。

我正在 iPhone 6 模拟器上运行 Xcode 6 和 iOS 8。

13个回答

1
/*
In SWIFT : Create an IBDesignable sub class of UIButton and override the intrinsicContentSize as shown below. It will resize appropriately for text and images. Then change your line break property to Word Wrap. Add a "fixedWidth" inspectable property that will adjust the height if you need buttons with fixed widths or when set to false will adjust the width and keep the height fixed.
*/

import UIKit

@IBDesignable
class UIButtonX: UIButton {
    
    //...

    @IBInspectable var fixedWidth : Bool = true
override var intrinsicContentSize: CGSize {
    let labelSize = titleLabel?.sizeThatFits(CGSize(width: fixedWidth ? frame.width : .greatestFiniteMagnitude, height: fixedWidth ? .greatestFiniteMagnitude : frame.height)) ?? .zero

    let wImage = image(for: [])?.size.width ?? 0
    let wTitleInset = titleEdgeInsets.left + titleEdgeInsets.right
    let wImageInset = imageEdgeInsets.left + imageEdgeInsets.right
    let wContentInset = contentEdgeInsets.left + contentEdgeInsets.right
    let width : CGFloat = labelSize.width + wImage + wTitleInset + wImageInset + wContentInset
    
    let biggerHeight = max(image(for: [])?.size.height ?? 0, labelSize.height)
    let hTitleInset = titleEdgeInsets.top + titleEdgeInsets.bottom
    let hImageInset = imageEdgeInsets.top + imageEdgeInsets.bottom
    let hContentInset = contentEdgeInsets.top + contentEdgeInsets.bottom
    let height : CGFloat = biggerHeight + hTitleInset + hImageInset + hContentInset
    
    let desiredButtonSize = CGSize(width: width, height: height)
    
    return desiredButtonSize
}
    
    //...

}

0
class SFResizableButton: UIButton {
  override var intrinsicContentSize: CGSize {
    get {
      var labelSize = CGSize.zero
        if let text = titleLabel?.text, let font = titleLabel?.font {
          labelSize.width = text.width(constrained: .greatestFiniteMagnitude, font: font)
        } else if let att = titleLabel?.attributedText {
          labelSize.width = att.width(constrained: .greatestFiniteMagnitude)
        }
      if let imageView = imageView {
        labelSize.width = labelSize.width + imageView.frame.width
      }
      let desiredButtonSize = CGSize(width: ceil(labelSize.width) + titleEdgeInsets.left + titleEdgeInsets.right + imageEdgeInsets.left + imageEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom + imageEdgeInsets.top + imageEdgeInsets.bottom)

        return desiredButtonSize
    }
  }
}
extesion String {
  func width(constrained height: CGFloat, font: UIFont) -> CGFloat {
    let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
      let boundingBox = (self as NSString).boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

      return boundingBox.width
  }
}

extension NSAttributedString {
  func width(constrained height: CGFloat) -> CGFloat {
    let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
      let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)

      return boundingBox.width
  }
}

你可能需要考虑在这段代码周围添加一些上下文(解释)。 - Neo
好的,作为一个常见的解决方案,当调用按钮标题时,我们应该知道标题是由字符串还是NSAttributedString设置的。因此我添加了值检查和cal扩展。最后,我们应该考虑按钮是否将图像设置为imageView,首先要考虑的是intrinsicContentSize应该正确。 - looseyi

0

我创建了一个子类,它在UITableViewCell内部运行良好。 如果您想要像截图中那样在按钮周围添加边框,这是必需的。

UIButton with line breaks and a border

import Foundation
import UIKit

class IntrinsicTitleButton: UIButton {
    
    override var intrinsicContentSize: CGSize {
       let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.width - titleEdgeInsets.left - titleEdgeInsets.right, height: .greatestFiniteMagnitude)) ?? .zero
       let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)

       return desiredButtonSize
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        invalidateIntrinsicContentSize()
    }
    
    func setup() {
        titleLabel?.lineBreakMode = .byWordWrapping
    }
    
    override func setTitle(_ title: String?, for state: UIControl.State) {
        super.setTitle(title, for: state)
        layoutIfNeeded()
    }
    
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接