Swift - UIButton z dwoma wierszami tekstu

95

Zastanawiałem się, czy możliwe jest utworzenie UIButton z dwoma wierszami tekstu. Każdy wiersz powinien mieć inny rozmiar czcionki. Pierwsza linia będzie miała 17 punktów, a druga 11 punktów. Próbowałem majstrować przy umieszczaniu dwóch etykiet wewnątrz UIButton, ale nie mogę zmusić ich do pozostania w granicach przycisku.

Próbuję to wszystko zrobić w kreatorze interfejsu użytkownika, a nie programowo.

Dzięki

Scott
źródło

Odpowiedzi:

252

Są dwa pytania.

Zastanawiałem się, czy możliwe jest utworzenie UIButton z dwoma wierszami tekstu

Jest to możliwe za pomocą scenorysu lub programowo.

Storyboard:

Zmień „Tryb łamania wiersza” na Zawijanie znaków lub Zawijanie słów i użyj klawisza Alt / Opcja + Enter , aby wprowadzić nowy wiersz w polu Tytuł UIButton.

wprowadź opis obrazu tutaj

Programowo:

override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        btnTwoLine?.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping;
}

Każdy wiersz powinien mieć inny rozmiar czcionki 1

W najgorszym przypadku możesz użyć własnej UIButtonklasy i dodać do niej dwie etykiety.

Lepszym sposobem jest skorzystanie z NSMutableAttributedString. Należy zauważyć, że można to osiągnąć tylko programowo.

Swift 5:

@IBOutlet weak var btnTwoLine: UIButton?

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    //applying the line break mode
    textResponseButton?.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping;
    let buttonText: NSString = "hello\nthere"

    //getting the range to separate the button title strings
    let newlineRange: NSRange = buttonText.range(of: "\n")

    //getting both substrings
    var substring1 = ""
    var substring2 = ""

    if(newlineRange.location != NSNotFound) {
        substring1 = buttonText.substring(to: newlineRange.location)
        substring2 = buttonText.substring(from: newlineRange.location)
    }

    //assigning diffrent fonts to both substrings
    let font1: UIFont = UIFont(name: "Arial", size: 17.0)!
    let attributes1 = [NSMutableAttributedString.Key.font: font1]
    let attrString1 = NSMutableAttributedString(string: substring1, attributes: attributes1)

    let font2: UIFont = UIFont(name: "Arial", size: 11.0)!
    let attributes2 = [NSMutableAttributedString.Key.font: font2]
    let attrString2 = NSMutableAttributedString(string: substring2, attributes: attributes2)

    //appending both attributed strings
    attrString1.append(attrString2)

    //assigning the resultant attributed strings to the button
    textResponseButton?.setAttributedTitle(attrString1, for: [])
}

Starszy Szybki

@IBOutlet weak var btnTwoLine: UIButton?

override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        //applying the line break mode
        btnTwoLine?.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping;

        var buttonText: NSString = "hello\nthere"

        //getting the range to separate the button title strings
        var newlineRange: NSRange = buttonText.rangeOfString("\n")

        //getting both substrings
        var substring1: NSString = ""
        var substring2: NSString = ""

        if(newlineRange.location != NSNotFound) {
            substring1 = buttonText.substringToIndex(newlineRange.location)
            substring2 = buttonText.substringFromIndex(newlineRange.location)
        }

        //assigning diffrent fonts to both substrings
        let font:UIFont? = UIFont(name: "Arial", size: 17.0)
        let attrString = NSMutableAttributedString(
            string: substring1 as String,
            attributes: NSDictionary(
                object: font!,
                forKey: NSFontAttributeName) as [NSObject : AnyObject])

        let font1:UIFont? = UIFont(name: "Arial", size: 11.0)
        let attrString1 = NSMutableAttributedString(
            string: substring2 as String,
            attributes: NSDictionary(
                object: font1!,
                forKey: NSFontAttributeName) as [NSObject : AnyObject])

        //appending both attributed strings
        attrString.appendAttributedString(attrString1)

        //assigning the resultant attributed strings to the button
        btnTwoLine?.setAttributedTitle(attrString, forState: UIControlState.Normal)

    }

Wynik

wprowadź opis obrazu tutaj

Shamsudheen TK
źródło
2
Działa świetnie. Zastanawiam się teraz, czy istnieje sposób na wyśrodkowanie tekstu w każdym wierszu i czy istnieje sposób, aby wstawić więcej odstępu między dwoma wierszami.
Scott
3
możesz wyrównać tekst linii do środka. napisz następujący kod btnTwoLine? .titleLabel? .textAlignment = NSTextAlignment.Center lub zrób to za pomocą pliku storyboardu (sekcja kontrolna-> Wyrównanie)
Shamsudheen TK
czy mogę wiedzieć, jaki jest cel wprowadzenia większej liczby linii pomiędzy?
Shamsudheen TK
To zależy od rozmiaru guzika. Jeśli przycisk jest duży, dwa wiersze tekstu będą znajdować się dokładnie pośrodku, z dużą ilością miejsca na górze i na dole. To nie był wygląd, do którego dążyłem.
Scott,
musisz zastosować tutaj kilka sztuczek :) możesz wstawić więcej linii pomiędzy używając wielu \ n. Mam na myśli, że "cześć \ n \ n \ ntutaj" da ci trzy spacje. jednak nie zapomnij zmodyfikować swojego kodu var newlineRange: NSRange = buttonText.rangeOfString ("\ n \ n \ n")
Shamsudheen TK
22

Szukałem prawie tego samego tematu, z tym że nie potrzebuję dwóch różnych rozmiarów czcionek. W przypadku, gdy ktoś szuka prostego rozwiązania:

    let button = UIButton()
    button.titleLabel?.numberOfLines = 0
    button.titleLabel?.lineBreakMode = .byWordWrapping
    button.setTitle("Foo\nBar", for: .normal)
    button.titleLabel?.textAlignment = .center
    button.sizeToFit()
    button.addTarget(self, action: #selector(rightBarButtonTapped), for: .allEvents)
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
Nico S.
źródło
13

Zauważyłem problem w większości rozwiązań polegający na tym, że podczas przełączania trybu łamania linii na "Zawijanie znaków" druga linia zostanie wyrównana do pierwszej linii

Aby wszystkie linie były wyśrodkowane. po prostu zmień tytuł ze zwykłego na przypisany, a następnie możesz wyśrodkować każdą linię

przypisany tytuł wyśrodkowany

Musa almatri
źródło
6

zmień podział wiersza na zawijanie znaków, wybierz przycisk iw Inspektorze atrybutów przejdź do podziału wiersza i zmień go na zawijanie znaków

wprowadź opis obrazu tutaj

Sabhay Sardana
źródło
6

Składnia SWIFT 3

let str = NSMutableAttributedString(string: "First line\nSecond Line")
str.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 17), range: NSMakeRange(0, 10))
str.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 12), range: NSMakeRange(11, 11))
button.setAttributedTitle(str, for: .normal)
Maksim Kniazev
źródło
2
nie wiem dlaczego, ale musiałem dodać button.titleLabel? .numberOfLines = 0
budidino
Najpierw nie zadziałało w Swift 4. Należy ustawić „podział wiersza” na „zawijanie wyrazów”. Dzięki stary :)
Karan Alangat,
Oryginalna wcześniejsza odpowiedź jest poniżej: stackoverflow.com/a/30679547/5318223
Kiril S.
5

Naprawiłem to i moje rozwiązanie było tylko w Storyboard.

Zmiany:

Dodano w Inspektor tożsamości -> Atrybuty środowiska uruchomieniowego zdefiniowane przez użytkownika (te KeyPaths):

  • numberOfLines = 2
  • titleLabel.textAlignment = 1

Atrybuty środowiska wykonawczego zdefiniowane przez użytkownika

Dodałem to w inspektorze atrybutów:

  • podział wiersza = zawijanie słów

Zawijanie tekstu

A. Trejo
źródło
2

Musisz to zrobić w kodzie. nie możesz ustawić 2 różnych czcionek w IB. Oprócz zmiany trybu łamania linii na zawijanie znaków, potrzebujesz czegoś takiego, aby ustawić tytuł,

override func viewDidLoad() {
        super.viewDidLoad()
        var str = NSMutableAttributedString(string: "First line\nSecond Line")
        str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(17), range: NSMakeRange(0, 10))
        str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(12), range: NSMakeRange(11, 11))
        button.setAttributedTitle(str, forState: .Normal)

    }
rdelmar
źródło
1

Myślę, że jednym ze sposobów jest użycie etykiet. Zrobiłem to i wydaje się, że działa dobrze. Mógłbym stworzyć to jako UIButton, a potem chyba wyeksponować etykiety. Nie wiem, czy to ma sens.

    let firstLabel = UILabel()

    firstLabel.backgroundColor = UIColor.lightGrayColor()
    firstLabel.text = "Hi"
    firstLabel.textColor = UIColor.blueColor()
    firstLabel.textAlignment = NSTextAlignment.Center
    firstLabel.frame = CGRectMake(0, testButton.frame.height * 0.25, testButton.frame.width, testButton.frame.height * 0.2)
    testButton.addSubview(firstLabel)

    let secondLabel = UILabel()

    secondLabel.backgroundColor = UIColor.lightGrayColor()
    secondLabel.textColor = UIColor.blueColor()
    secondLabel.font = UIFont(name: "Arial", size: 12)
    secondLabel.text = "There"
    secondLabel.textAlignment = NSTextAlignment.Center
    secondLabel.frame = CGRectMake(0, testButton.frame.height * 0.5, testButton.frame.width, testButton.frame.height * 0.2)
    testButton.addSubview(secondLabel)
Scott
źródło
0

Moja droga:

func setButtonTitle(title: String, subtitle: String, button: UIButton){
        //applying the line break mode
        button.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping;
        let title = NSMutableAttributedString(string: title, attributes: Attributes.biggestLabel)
        let subtitle = NSMutableAttributedString(string: subtitle, attributes: Attributes.label)
        let char = NSMutableAttributedString(string: "\n", attributes: Attributes.biggestLabel)
        title.append(char)
        title.append(subtitle)
        button.setAttributedTitle(title, for: .normal)
    }
nastassia
źródło
0

Sugerowane rozwiązania niestety nie zadziałały, gdy chciałem mieć przycisk mutliline wewnątrz CollectionView. Następnie kolega pokazał mi obejście, którym chciałbym się podzielić na wypadek, gdyby ktoś miał ten sam problem - mam nadzieję, że to pomoże! Utwórz klasę, która dziedziczy po UIControl i rozszerz ją o etykietę, która będzie wtedy zachowywać się podobnie jak przycisk.

class MultilineButton: UIControl {

    let label: UILabel = {
        $0.translatesAutoresizingMaskIntoConstraints = false
        $0.numberOfLines = 0
        $0.textAlignment = .center
        return $0
    }(UILabel())

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

        addSubview(label)

        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
            label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
            label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
        ])
    }

    override var isHighlighted: Bool {
        didSet {
            backgroundColor = backgroundColor?.withAlphaComponent(isHighlighted ? 0.7 : 1.0)
            label.textColor = label.textColor.withAlphaComponent(isHighlighted ? 0.7 : 1.0)
        }
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
Mitemmetim
źródło