ストーリーボードのView Controllerのビューに追加する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];
結果は以下のとおりです。
ボタンのコンテンツに合わせてサイズを変更したい。これどうやってするの?
私はもう試した
[self.button sizeToFit];
そして、コンテンツのハグと圧縮抵抗の自動レイアウト制約の優先順位を必須に設定しようとしました。
また、contentEdgeInsets
とtitleEdgeInsets
をUIEdgeInsetsZero
に明示的に設定し、invalidateIntrinsicContentSize
を呼び出してみました。
また、タイトル文字列に改行文字を配置すると、ボタンがコンテンツに合わせてサイズ変更されるように見えることにも気付きました。
IPhone 6シミュレーターのXcode 6およびiOS 8で実行しています。
これは機能するようになりましたが、システムタイプではなく、カスタムボタンを使用する必要があります。ボタンに幅と高さの両方の制約を与え、IBOutletを高さの制約(コードではheightCon)にして、コードで調整できるようにします。
- (void)viewDidLoad {
[super viewDidLoad];
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 addTarget:self action:@selector(doStuff:) forControlEvents:UIControlEventTouchUpInside];
self.button.backgroundColor = [UIColor redColor];
self.button.titleLabel.backgroundColor = [UIColor blueColor];
[self.button layoutIfNeeded]; // need this to update the button's titleLabel's size
self.heightCon.constant = self.button.titleLabel.frame.size.height;
}
編集後:
サブクラスを作成してこのコードを使用する場合は、システムボタンを使用してこれをより簡単に行うこともできます。
@implementation RDButton
-(CGSize)intrinsicContentSize {
return CGSizeMake(self.frame.size.width, self.titleLabel.frame.size.height);
}
タイトルを設定すると、オーバーライドされたintrinsicContentSizeメソッドが呼び出されます。この場合、高さの制約を設定しないでください。
Swift 4.xバージョンの Kubba's answer:
Interface BuilderでLine BreakをClip/WordWrap /に更新する必要がある対応するボタン。
class ResizableButton: UIButton {
override var intrinsicContentSize: CGSize {
let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.width, height: .greatestFiniteMagnitude)) ?? .zero
let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)
return desiredButtonSize
}
}
同じ問題になりました。 UIButton
のtitleLabel
に複数の行がある場合にのみ発生します。 UIKitのバグですか?
My Swiftソリューション:
class ResizableButton: UIButton {
override var intrinsicContentSize: CGSize {
let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.size.width, height: CGFloat.greatestFiniteMagnitude)) ?? .zero
let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)
return desiredButtonSize
}
}
私はこれにしばらく苦労し、UIButtonをサブクラス化し、これらの2つの関数を追加することで動作するようになりました
class GoalsButton: UIButton {
override var intrinsicContentSize: CGSize {
return self.titleLabel!.intrinsicContentSize
}
// Whever the button is changed or needs to layout subviews,
override func layoutSubviews() {
super.layoutSubviews()
titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
}
}
私は自分のコンピューターから離れているので、今はコードを追加できませんが、この回避策を以前に見つけました。
できることは、UILabel
を作成し、UITapGestureRecognizer
をラベルに追加することです。タップイベントを処理することにより、ボタンのアクションに必要な処理を実行します。また、UILabel
でユーザーインタラクションを有効にしてください。
これで、このラベルは自動サイズ変更ボタンとして動作します。
複数行のテキストでUIButton
でも同じ問題があり、画像もありました。 sizeThatFits:
を使用してサイズを計算しましたが、間違った高さを計算しました。
私はそれをUIButtonTypeCustom
にせず、ボタンのtitleLabel
でsizeThatFits:
を呼び出しましたが、ボタンの幅が小さい(ボタン内の画像のため):
CGSize buttonSize = [button sizeThatFits:CGSizeMake(maxWidth, maxHeight)];
CGSize labelSize = [button.titleLabel sizeThatFits:CGSizeMake(maxWidth - offset, maxHeight)]; // offset for image
buttonSize.height = labelSize.height;
buttonFrame.size = buttonSize;
そして、そのサイズの高さを使用してボタンのフレームを正しく設定しましたが、うまくいきました:)
UIButton
の内部サイズ設定にバグがある可能性があります。
[self.button sizeToFit]は、自動レイアウトがオフになっている場合に機能するはずです。自動レイアウトを使用する必要がある場合は、他の提案(線幅の計算)などがより適切です。
私が提案するのは、テキストの幅を計算し、自分でフレームを計算することです。とにかく複雑ではありません。最初にテキストの幅を取得します。
[NSString sizeWithFont:font];
Mod操作を実行すると、テキストの行数が簡単にわかります。
この方法は、iOS 7以前、iOS 7、および試してみたい場合に使用します。
[NSString sizeWithAttributes:aDictionary];
以下に示すように、カスタムUIButtonのCGSize)intrinsicContentSizeをオーバーライドします。
---(目的-C:
-(CGSize)intrinsicContentSize {
CGSize titleLabelIntrinsicSize = [self.titleLabel intrinsicContentSize];
return CGSizeMake(titleLabelIntrinsicSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, titleLabelIntrinsicSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom);
}
Swfit:
override var intrinsicContentSize: CGSize {
get {
if let thisSize = self.titleLabel?.intrinsicContentSize {
return CGSize(width: thisSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, height: thisSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom)
}
return super.intrinsicContentSize
}
}
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
}
}
ビューをレイアウトするときに固有のコンテンツサイズを無効にしてから、intrinsicContentSizeプロパティのボタンの高さを計算する必要がありました。
Swift3/Xcode 9のコードは次のとおりです。
override func layoutSubviews() {
self.invalidateIntrinsicContentSize()
super.layoutSubviews()
}
override var intrinsicContentSize: CGSize {
return CGSize(width: self.frame.size.width, height: titleLabel!.frame.size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}