Подтвердить что ты не робот

Многострочный UIButton и автозапуск

Я создал контроллер вида, который выглядит так:

enter image description here

Я хочу, чтобы две верхние кнопки всегда имели 20 точек между собой и левыми/правыми краями всего изображения. Они должны всегда иметь одинаковую ширину. Я создал ограничения для всего этого, и он работает именно так, как я этого хочу. Проблема заключается в вертикальных ограничениях. Кнопки всегда должны быть на 20 пунктов ниже верхнего края. Они должны иметь одинаковую высоту. Однако автоопределение не учитывает, что левой метке нужны две строки, чтобы соответствовать всему ее тексту, поэтому результат выглядит следующим образом:

enter image description here

Я хочу, чтобы это было на первом рисунке. Я не могу добавить постоянные ограничения высоты для кнопок, потому что, когда приложение работает на iPad, требуется только одна строка, и тогда было бы бесполезно иметь дополнительное пространство.

В viewDidLoad Я пробовал это:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.leftButton.titleLabel.preferredMaxLayoutWidth = (self.view.frame.size.width - 20.0 * 3) / 2.0;
    self.rightButton.titleLabel.preferredMaxLayoutWidth = (self.view.frame.size.width - 20.0 * 3) / 2.0;
}

Но это ничего не меняло.

Вопрос: как сделать автозапуск уважением, что левой кнопке нужны две строки?

4b9b3361

Ответ 1

У меня была такая же проблема, когда я хотел, чтобы моя кнопка развивалась вместе с ее названием. Мне пришлось подстроить UIButton и его intrinsicContentSize, чтобы он возвращал внутренний размер метки.

- (CGSize)intrinsicContentSize
{
    return self.titleLabel.intrinsicContentSize;
}

Так как UILabel является многострочным, его intrinsicContentSize неизвестен, и вы должны установить его preferredMaxLayoutWidth См. статью objc.io об этом

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.titleLabel.preferredMaxLayoutWidth = self.titleLabel.frame.size.width;
    [super layoutSubviews];
}

Остальная часть макета должна работать. Если вы установили обе кнопки с одинаковой высотой, другая будет расти. Полная кнопка выглядит так:

@implementation TAButton

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        self.titleLabel.numberOfLines = 0;
        self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
    }
    return self;
}

- (CGSize)intrinsicContentSize
{
    return self.titleLabel.intrinsicContentSize;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.titleLabel.preferredMaxLayoutWidth = self.titleLabel.frame.size.width;
    [super layoutSubviews];
}

@end

Ответ 2

Swift 4.1.2 Версия основана на ответе @Jan.

import UIKit

class MultiLineButton: UIButton {

    // MARK: - Init

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.commonInit()
    }

    private func commonInit() {
        self.titleLabel?.numberOfLines = 0
        self.titleLabel?.lineBreakMode = .byWordWrapping
    }

    // MARK: - Overrides

    override var intrinsicContentSize: CGSize {
        get {
             return titleLabel?.intrinsicContentSize ?? CGSize.zero
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        titleLabel?.preferredMaxLayoutWidth = titleLabel?.frame.size.width ?? 0
        super.layoutSubviews()
    }

}

Ответ 3

Это касается вложений в области содержимого и работает для меня:

class MultilineButton: UIButton {

    func setup() {
        self.titleLabel?.numberOfLines = 0
        self.setContentHuggingPriority(UILayoutPriorityDefaultLow + 1, for: .vertical)
        self.setContentHuggingPriority(UILayoutPriorityDefaultLow + 1, for: .horizontal)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    override var intrinsicContentSize: CGSize {
        let size = self.titleLabel!.intrinsicContentSize
        return CGSize(width: size.width + contentEdgeInsets.left + contentEdgeInsets.right, height: size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
    }
}

Ответ 4

Полный класс в Swift 3 - на основе @Jan, @Quantaliinuxite и @matt bezark:

@IBDesignable
class MultiLineButton:UIButton {

    //MARK: -
    //MARK: Setup
    func setup () {
        self.titleLabel?.numberOfLines = 0

        //The next two lines are essential in making sure autolayout sizes us correctly
        self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, for: .vertical)
        self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, for: .horizontal)
    }

    //MARK:-
    //MARK: Method overrides
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    override var intrinsicContentSize: CGSize {
        return self.titleLabel!.intrinsicContentSize
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
    }
}

Ответ 5

ОБНОВЛЕННАЯ версия Swift/Swift 2.0 снова на основе ответа @Jan

@IBDesignable
class MultiLineButton:UIButton {

  //MARK: -
  //MARK: Setup
  func setup () {
    self.titleLabel?.numberOfLines = 0

    //The next two lines are essential in making sure autolayout sizes us correctly
    self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, forAxis: .Vertical) 
    self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, forAxis: .Horizontal)
  }

  //MARK:-
  //MARK: Method overrides
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
  }

  override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
  }

  override func intrinsicContentSize() -> CGSize {
    return self.titleLabel!.intrinsicContentSize()
  }

  override func layoutSubviews() {
    super.layoutSubviews()
    titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
  }
}

Ответ 6

для Swift 3.1

intrisicContentSize - это свойство вместо функции

override var intrinsicContentSize: CGSize {
    return self.titleLabel!.intrinsicContentSize
}

Ответ 7

добавьте отсутствующие ограничения:

if let label = button.titleLabel {

    button.addConstraint(NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: button, attribute: .top, multiplier: 1.0, constant: 0.0))
    button.addConstraint(NSLayoutConstraint(item: label, attribute: .bottom, relatedBy: .equal, toItem: button, attribute: .bottom, multiplier: 1.0, constant: 0.0))
}

Ответ 8

Простое решение, работающее на меня: заставить многострочную кнопку учитывать высоту заголовка в Swift 4.2, добавив ограничение для высоты кнопки на основе высоты надписи заголовка:

let height = NSLayoutConstraint(item: multilineButton,
                               attribute: .height,
                               relatedBy: .equal,
                               toItem: multilineButton.titleLabel,
                               attribute: .height,
                               multiplier: 1,
                               constant: 0)
multilineButton.addConstraint(height)

Ответ 9

Вместо того, чтобы дважды вызывать layoutSubviews, я бы вычислял preferredMaxLayoutWidth MaxLayoutWidth вручную

@objcMembers class MultilineButton: UIButton {

override var intrinsicContentSize: CGSize {
    // override to have the right height with autolayout
    get {
        var titleContentSize = titleLabel!.intrinsicContentSize
        titleContentSize.height += contentEdgeInsets.top + contentEdgeInsets.bottom
        return titleContentSize
    }
}

override func awakeFromNib() {
    super.awakeFromNib()
    titleLabel!.numberOfLines = 0
}

override func layoutSubviews() {
    let contentWidth = width - contentEdgeInsets.left - contentEdgeInsets.right
    let imageWidth = imageView?.width ?? 0 + imageEdgeInsets.left + imageEdgeInsets.right
    let titleMaxWidth = contentWidth - imageWidth - titleEdgeInsets.left - titleEdgeInsets.right

    titleLabel!.preferredMaxLayoutWidth = titleMaxWidth
    super.layoutSubviews()
}
}

Ответ 10

Вы пробовали использовать это:

self.leftButton.titleLabel.textAlignment = NSTextAlignmentCenter;
self.leftButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping | NSLineBreakByTruncatingTail;
self.leftButton.titleLabel.numberOfLines = 0;

Ответ 11

@Ответ для меня не работает (по крайней мере) для iOS 8.1, 9.0 с Xcode 9.1. Проблема: titleLabel -intrinsicContentSize возвращает очень большую ширину и небольшую высоту, так как ограничение ширины вообще отсутствует (titleLabel.frame при вызове имеет нулевой размер, что приводит к проблеме измерений). Более того, он не учитывает возможные вставки и/или изображение.

Итак, вот моя реализация, которая должна исправить все вещи (нужен только один метод):

@implementation PRButton

- (CGSize)intrinsicContentSize
{
    CGRect titleFrameMax = UIEdgeInsetsInsetRect(UIEdgeInsetsInsetRect(UIEdgeInsetsInsetRect(
        self.bounds, self.alignmentRectInsets), self.contentEdgeInsets), self.titleEdgeInsets
    );
    CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(titleFrameMax.size.width, CGFLOAT_MAX)];

    CGSize superSize = [super intrinsicContentSize];
    return CGSizeMake(
        titleSize.width + (self.bounds.size.width - titleFrameMax.size.width),
        MAX(superSize.height, titleSize.height + (self.bounds.size.height - titleFrameMax.size.height))
    );
}

@end

Ответ 12

Существует решение без создания подклассов на iOS11. Просто нужно установить одно дополнительное ограничение в коде, чтобы соответствовать высоте button и button.titleLabel.

ObjC:

// In init or overriden updateConstraints method
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.button
                                                              attribute:NSLayoutAttributeHeight
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:self.button.titleLabel
                                                              attribute:NSLayoutAttributeHeight
                                                             multiplier:1
                                                               constant:0];

[self addConstraint:constraint];

И в некоторых случаях (как сказано ранее):

- (void)layoutSubviews {
    [super layoutSubviews];

    self.button.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.button.titleLabel.frame);
}

Swift:

let constraint = NSLayoutConstraint(item: button,
                                    attribute: .height,
                                    relatedBy: .equal,
                                    toItem: button.titleLabel,
                                    attribute: .height,
                                    multiplier: 1,
                                    constant: 0)

self.addConstraint(constraint)

+

override func layoutSubviews() {
    super.layoutSubviews()

    button.titleLabel.preferredMaxLayoutWidth = button.titleLabel.frame.width
}

Ответ 13

//Swift 4 - Create Dynamic Button MultiLine Dynamic

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

         /// Add DemoButton 1
        let demoButton1 = buildButton("Demo 1")
        //demoButton1.addTarget(self, action: #selector(ViewController.onDemo1Tapped), for: .touchUpInside)
        view.addSubview(demoButton1)

        view.addConstraint(NSLayoutConstraint(item: demoButton1, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: demoButton1, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: -180))

    }

    func buildButton(_ title: String) -> UIButton {
        let button = UIButton(type: .system)
        button.backgroundColor = UIColor(red: 80/255, green: 70/255, blue: 66/255, alpha: 1.0)

        //--------------------------
        //to make the button multiline
        //button.titleLabel!.lineBreakMode = .byWordWrapping
        button.titleLabel?.textAlignment = .center
        button.titleLabel?.numberOfLines = 0
        //button.titleLabel?.adjustsFontSizeToFitWidth = true
        //button.sizeToFit()
        button.titleLabel?.preferredMaxLayoutWidth = self.view.bounds.width//200
        button.layer.borderWidth = 2
        let height = NSLayoutConstraint(item: button,
                                        attribute: .height,
                                        relatedBy: .equal,
                                        toItem: button.titleLabel,
                                        attribute: .height,
                                        multiplier: 1,
                                        constant: 0)
        button.addConstraint(height)
        //--------------------------

        button.setTitle(title, for: UIControlState())
        button.layer.cornerRadius = 4.0
        button.setTitleColor(UIColor(red: 233/255, green: 205/255, blue: 193/255, alpha: 1.0), for: UIControlState())
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }
}