web-dev-qa-db-ja.com

titleLabelに合わせてサイズを変更するUIButton

ストーリーボードの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];

結果は以下のとおりです。

enter image description here

ボタンのコンテンツに合わせてサイズを変更したい。これどうやってするの?

私はもう試した

[self.button sizeToFit];

そして、コンテンツのハグと圧縮抵抗の自動レイアウト制約の優先順位を必須に設定しようとしました。

また、contentEdgeInsetstitleEdgeInsetsUIEdgeInsetsZeroに明示的に設定し、invalidateIntrinsicContentSizeを呼び出してみました。

また、タイトル文字列に改行文字を配置すると、ボタンがコンテンツに合わせてサイズ変更されるように見えることにも気付きました。

IPhone 6シミュレーターのXcode 6およびiOS 8で実行しています。

31
Darren

これは機能するようになりましたが、システムタイプではなく、カスタムボタンを使用する必要があります。ボタンに幅と高さの両方の制約を与え、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メソッドが呼び出されます。この場合、高さの制約を設定しないでください。

18
rdelmar

Swift 4.xバージョンの Kubba's answer:

Interface BuilderでLine BreakClip/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
    }
}
24
A. Buksha

同じ問題になりました。 UIButtontitleLabelに複数の行がある場合にのみ発生します。 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
    }
}
14
Kubba

私はこれにしばらく苦労し、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
    }
}
3
Lord Fresh

私は自分のコンピューターから離れているので、今はコードを追加できませんが、この回避策を以前に見つけました。

できることは、UILabelを作成し、UITapGestureRecognizerをラベルに追加することです。タップイベントを処理することにより、ボタンのアクションに必要な処理を実行します。また、UILabelでユーザーインタラクションを有効にしてください。

これで、このラベルは自動サイズ変更ボタンとして動作します。

2
Vinay Jain

複数行のテキストでUIButtonでも同じ問題があり、画像もありました。 sizeThatFits:を使用してサイズを計算しましたが、間違った高さを計算しました。

私はそれをUIButtonTypeCustomにせず、ボタンのtitleLabelsizeThatFits:を呼び出しましたが、ボタンの幅が小さい(ボタン内の画像のため):

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の内部サイズ設定にバグがある可能性があります。

1
BSevo

[self.button sizeToFit]は、自動レイアウトがオフになっている場合に機能するはずです。自動レイアウトを使用する必要がある場合は、他の提案(線幅の計算)などがより適切です。

1
Aamir

私が提案するのは、テキストの幅を計算し、自分でフレームを計算することです。とにかく複雑ではありません。最初にテキストの幅を取得します。

[NSString sizeWithFont:font];

Mod操作を実行すると、テキストの行数が簡単にわかります。

この方法は、iOS 7以前、iOS 7、および試してみたい場合に使用します。

[NSString sizeWithAttributes:aDictionary];
1
Xiangdong

以下に示すように、カスタム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
        }
    }
1
Bhavesh Patel
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
  }
}
0
looseyi

ビューをレイアウトするときに固有のコンテンツサイズを無効にしてから、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)
}
0
Maria