Come posso fare in modo che un pulsante abbia un bordo arrotondato in Swift?


232

Sto costruendo un'app usando swift nell'ultima versione di Xcode 6 e vorrei sapere come posso modificare il mio pulsante in modo che possa avere un bordo arrotondato che potrei adattare da solo se necessario. Una volta fatto, come posso cambiare il colore del bordo stesso senza aggiungere uno sfondo ad esso? In altre parole, voglio un pulsante leggermente arrotondato senza sfondo, solo un bordo 1pt di un certo colore.

Risposte:


528

Utilizzare button.layer.cornerRadius, button.layer.borderColore button.layer.borderWidth. Nota che borderColorrichiede un CGColor, quindi potresti dire (Swift 3/4):

button.backgroundColor = .clear
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.black.cgColor

2
Ho provato questo, ma ho un piccolo problema il primo testo sta iniziando dal bordo stesso, quindi non è così bello c'è un modo per risolverlo
vinbhai4u

2
@ vinbhai4u Prova a usare titleEdgeInsets.
Ritorna vero

come possiamo rendere la scatola più grande del testo del pulsante?
Prima il

creare uno sbocco per il pulsante per fare riferimento ad esso
Jobs

voglio usarlo in questo modo ma non funziona :( UIButton.appearance (). layer.cornerRadius = 4
MR

65

Per fare questo lavoro nello storyboard (Interface Builder Inspector)

Con l'aiuto di IBDesignable, possiamo aggiungere più opzioni a Interface Builder Inspector per UIButtone modificarle sullo storyboard. Innanzitutto, aggiungi il seguente codice al tuo progetto.

@IBDesignable extension UIButton {

    @IBInspectable var borderWidth: CGFloat {
        set {
            layer.borderWidth = newValue
        }
        get {
            return layer.borderWidth
        }
    }

    @IBInspectable var cornerRadius: CGFloat {
        set {
            layer.cornerRadius = newValue
        }
        get {
            return layer.cornerRadius
        }
    }

    @IBInspectable var borderColor: UIColor? {
        set {
            guard let uiColor = newValue else { return }
            layer.borderColor = uiColor.cgColor
        }
        get {
            guard let color = layer.borderColor else { return nil }
            return UIColor(cgColor: color)
        }
    }
}

Quindi imposta semplicemente gli attributi per i pulsanti sullo storyboard.

inserisci qui la descrizione dell'immagine


4
Questa è più o meno una copia della risposta di seguito di Hamza Ghazouani.
Ritorna vero

quando uso la soluzione di cui sopra per boarderColor, la sua compilazione non è riuscita in Interfacebuilder, utilizzo il codice seguente ma ho successo se ho usato didset anziché impostare e ottenere. @IBInspectable var borderColor: UIColor = .red {// didSet {// layer.borderColor = borderColor.cgColor //} set {guard let uiColor = newValue else {return} layer.borderColor = uiColor.cgColor} get {guard let color = layer.borderColor else {return nil} return UIColor (cgColor: color)}}
Amr Angry,

1
Wow, come per magia! Grazie! Googler: assicurati di inserire questo snippet di codice completamente al di fuori dell'ultima parentesi graffa nel file in cui lo stai posizionando - lo snippet di codice non dovrebbe essere all'interno di alcuna classe o funzione
velkoon

24

Ho creato una semplice sottoclasse di UIButton che usa il tintColorper i suoi colori di testo e bordo e quando evidenziato cambia il suo sfondo in tintColor.

class BorderedButton: UIButton {

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    layer.borderWidth = 1.0
    layer.borderColor = tintColor.CGColor
    layer.cornerRadius = 5.0
    clipsToBounds = true
    contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
    setTitleColor(tintColor, forState: .Normal)
    setTitleColor(UIColor.whiteColor(), forState: .Highlighted)
    setBackgroundImage(UIImage(color: tintColor), forState: .Highlighted)
}
}

Questo fa uso di un'estensione UIImage che crea un'immagine da un colore, ho trovato quel codice qui: https://stackoverflow.com/a/33675160

Funziona meglio quando impostato su Tipo personalizzato nel builder di interfaccia poiché il tipo di sistema predefinito modifica leggermente i colori quando il pulsante è evidenziato.


13

Questa classe si basa su tutti i commenti e suggerimenti nelle risposte e può anche essere configurabile direttamente da xcode. Copia nel tuo progetto e inserisci qualsiasi UIButton e modifica per utilizzare la classe personalizzata, ora seleziona solo il bordo o il colore di sfondo da xcode per gli stati normali e / o evidenziati.

//
//  RoundedButton.swift
//

import UIKit

@IBDesignable
class RoundedButton:UIButton {

    @IBInspectable var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    //Normal state bg and border
    @IBInspectable var normalBorderColor: UIColor? {
        didSet {
            layer.borderColor = normalBorderColor?.CGColor
        }
    }

    @IBInspectable var normalBackgroundColor: UIColor? {
        didSet {
            setBgColorForState(normalBackgroundColor, forState: .Normal)
        }
    }


    //Highlighted state bg and border
    @IBInspectable var highlightedBorderColor: UIColor?

    @IBInspectable var highlightedBackgroundColor: UIColor? {
        didSet {
            setBgColorForState(highlightedBackgroundColor, forState: .Highlighted)
        }
    }


    private func setBgColorForState(color: UIColor?, forState: UIControlState){
        if color != nil {
            setBackgroundImage(UIImage.imageWithColor(color!), forState: forState)

        } else {
            setBackgroundImage(nil, forState: forState)
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        layer.cornerRadius = layer.frame.height / 2
        clipsToBounds = true

        if borderWidth > 0 {
            if state == .Normal && !CGColorEqualToColor(layer.borderColor, normalBorderColor?.CGColor) {
                layer.borderColor = normalBorderColor?.CGColor
            } else if state == .Highlighted && highlightedBorderColor != nil{
                layer.borderColor = highlightedBorderColor!.CGColor
            }
        }
    }

}

//Extension Required by RoundedButton to create UIImage from UIColor
extension UIImage {
    class func imageWithColor(color: UIColor) -> UIImage {
        let rect: CGRect = CGRectMake(0, 0, 1, 1)
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), false, 1.0)
        color.setFill()
        UIRectFill(rect)
        let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

1
Perché l'importazione Foundationdurante l'importazione è UIKitdavvero sufficiente?
Ritorna vero

@returntrue Sì, hai ragione, Foundation non è richiesto, vengo dal modello base xcode originale se non sbaglio. Ho aggiornato il codice.
le0diaz,

L'unica cosa è che questo renderà il pulsante circolare tutto il tempo. Forse puoi aggiungere una proprietà raggio d'angolo per risolvere anche questo.
Andreas777,

10

Basato sulla risposta di @returntrue, sono riuscito a implementarlo in Interface Builder.

Per ottenere il pulsante degli angoli arrotondati usando Interface Builder aggiungi una chiave Path = "layer.cornerRadius"con Type = "Number"e Value = "10"(o altro valore come richiesto) nella " User Defined RunTime Attribute" del Identity Inspectorpulsante.


1
Questo funziona davvero, ma non è specifico di Swift. La domanda chiede esplicitamente come ottenere l'effetto desiderato usando Swift, non usando lo storyboard o simili.
restituisce il vero

2
La mia risposta non era indirizzata all'OP ma ad altri lettori che hanno affrontato il mio problema. e solo trovando questo post sono stato in grado di risolvere il problema. Secondo la mia opinione ed esperienza, le risposte non devono essere esattamente ciò che l'OP ha chiesto, purché siano correlate ad esso e forniscano informazioni supplementari utili ai lettori. Votando tali risposte si scoraggiano le risposte utili.
Zvi,

1
Sebbene ciò sia generalmente vero, specialmente per questioni generali, questa domanda è chiara nel suo carattere. Esistono una moltitudine di domande che richiedono una soluzione esplicita tramite lo storyboard e, se possibile, i lettori volevano avere una soluzione che utilizza lo storyboard, quindi aggiungere "storyboard" ai termini di ricerca di Google è sufficiente per mostrare queste domande. Non sto dicendo che la tua risposta sia negativa, ma piuttosto nel posto sbagliato.
restituisce il vero

Da quando ho trovato questo post il più vicino ad affrontare il mio problema, l'ho pubblicato qui. Seguire la mia strada significa che avrei dovuto scrivere un nuovo post e rispondere al mio post, che mi sembra un po 'eccessivo.
Zvi,

1
Ho trovato quelle domande che sembrano risolvere il tuo problema meglio di questa: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
restituisce il vero

6

È possibile utilizzare questa sottoclasse di UIButton per personalizzare UIButton secondo le proprie esigenze.

visitare questo repository github per riferimento

class RoundedRectButton: UIButton {

    var selectedState: Bool = false

    override func awakeFromNib() {
        super.awakeFromNib()
        layer.borderWidth = 2 / UIScreen.main.nativeScale
        layer.borderColor = UIColor.white.cgColor
        contentEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    }


    override func layoutSubviews(){
        super.layoutSubviews()
        layer.cornerRadius = frame.height / 2
        backgroundColor = selectedState ? UIColor.white : UIColor.clear
        self.titleLabel?.textColor = selectedState ? UIColor.green : UIColor.white
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        selectedState = !selectedState
        self.layoutSubviews()
    }
}

chi downvoted questo, sei stato anche cercare di lavorare in questo modo ??? solo non sprecare il vostro tempo downvoting
Nikesh Jha

Perché un downvote? questo sembra funzionare ed è molto pulito.
ColdGrub1384,

@ ColdGrub1384 esattamente :)
Nikesh Jha,

3

Penso che il modo più semplice e pulito sia usare il protocollo per evitare l'ereditarietà e la ripetizione del codice. Puoi modificare queste proprietà direttamente dallo storyboard

protocol Traceable {
    var cornerRadius: CGFloat { get set }
    var borderColor: UIColor? { get set }
    var borderWidth: CGFloat { get set }
}

extension UIView: Traceable {

    @IBInspectable var cornerRadius: CGFloat {
        get { return layer.cornerRadius }
        set {
            layer.masksToBounds = true
            layer.cornerRadius = newValue
        }
    }

    @IBInspectable var borderColor: UIColor? {
        get {
            guard let cgColor = layer.borderColor else { return nil }
            return  UIColor(cgColor: cgColor)
        }
        set { layer.borderColor = newValue?.cgColor }
    }

    @IBInspectable var borderWidth: CGFloat {
        get { return layer.borderWidth }
        set { layer.borderWidth = newValue }
    }
}

Aggiornare

In questo collegamento è possibile trovare un esempio con l'utilità del protocollo tracciabile

inserisci qui la descrizione dell'immagine


2
Non hai davvero bisogno del protocollo qui. Funziona perfettamente senza di essa.
restituire il

2
Ciao @returntrue, non so perché hai annullato il voto della mia risposta ... Penso che tu non l'abbia capito, ti invito a guardare questa sessione: developer.apple.com/videos/play/wwdc2015/408 Perché usare il protocollo? il protocollo consente una maggiore flessibilità, posso avere un'implementazione predefinita, ecc. La mia risposta consente di modificare queste proprietà nello storyboard e non ho bisogno di ripetere il codice o sottoclassare qualsiasi classe La tua risposta è corretta ma qui ho condiviso un altro modo di fare declassate tutte le altre risposte, dovreste ricordare che StackOverflow serve a condividere le nostre conoscenze prima di guadagnare reputazione
Hamza,

2
Corretto, il tuo codice consente tutte queste cose che hai elencato. MA, poiché UIView è la superclasse di tutti gli elementi dell'interfaccia utente, l'estensione UIView è sufficiente per svolgere il lavoro. Il protocollo Tracciabile non è richiesto in alcun modo e non comporta alcun miglioramento (poiché si traduce semplicemente in UIView: solo UIVview e le sue sottoclassi sono tracciabili).
restituire il

1
ciao @returntrue, hai ragione in questo caso d'uso, potrebbe essere, non abbiamo bisogno del protocollo, ma con esso abbiamo una maggiore flessibilità e, ad esempio, possiamo aggiungere valori predefiniti da applicare e disponibili solo per UIImageView e UIButton, I aggiornerò la mia risposta con questo esempio :)
Hamza,

1
Non ha molto senso applicare valori predefiniti per proprietà come il raggio dell'angolo o il colore del bordo. Tali valori devono essere applicati a ciascuna istanza della vista nell'interfaccia utente separatamente in modo significativo.
restituire il

3
   @IBOutlet weak var yourButton: UIButton! {
        didSet{
            yourButton.backgroundColor = .clear
            yourButton.layer.cornerRadius = 10
            yourButton.layer.borderWidth = 2
            yourButton.layer.borderColor = UIColor.white.cgColor
        }
    }

1

come suggerimento a parte assicurati che il tuo pulsante non sia una sottoclasse di alcuna classe personalizzata nella storyboard, in tal caso il tuo posto migliore per il codice dovrebbe essere nella classe personalizzata perché il codice di auto causa funziona solo fuori dalla classe personalizzata se il tuo pulsante è una sottoclasse di default Classe UIButton e presa di esso, spero che questo possa aiutare chiunque si chieda perché le radio d'angolo non si applicano al mio pulsante dal codice.


0
@IBOutlet weak var button: UIButton!

...

Penso che per il raggio basta questo parametro:

button.layer.cornerRadius = 5

Questo non aggiunge nulla di nuovo, a quanto pare.
Ritorna vero

0

PROVA QUESTO Bordo Bottone Con Angoli Arrotondati

anyButton.backgroundColor = .clear

anyButton.layer.cornerRadius = anyButton.frame.height / 2

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor

sì, ma introduce l'idea di usare un cornerRadius che è dinamico, non penso che sia stato semplicemente copiato e incollato.
benc,

0
import UIKit

@IBDesignable
class RoundedButton: UIButton {
    
    @IBInspectable var cornerRadius: CGFloat = 8
    @IBInspectable var borderColor: UIColor? = .lightGray
    
    override func draw(_ rect: CGRect) {
        layer.cornerRadius = cornerRadius
        layer.masksToBounds = true
        layer.borderWidth = 1
        layer.borderColor = borderColor?.cgColor
        
    }
    
    
}

-1

È possibile eseguire la sottoclasse UIButtone aggiungere @IBInspectablevariabili in modo da poter configurare i parametri dei pulsanti personalizzati tramite StoryBoard "Attribute Inspector". Di seguito scrivo quel codice.

@IBDesignable
class BHButton: UIButton {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */

    @IBInspectable lazy var isRoundRectButton : Bool = false

    @IBInspectable public var cornerRadius : CGFloat = 0.0 {
        didSet{
            setUpView()
        }
    }

    @IBInspectable public var borderColor : UIColor = UIColor.clear {
        didSet {
            self.layer.borderColor = borderColor.cgColor
        }
    }

    @IBInspectable public var borderWidth : CGFloat = 0.0 {
        didSet {
            self.layer.borderWidth = borderWidth
        }
    }

    //  MARK:   Awake From Nib

    override func awakeFromNib() {
        super.awakeFromNib()
        setUpView()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setUpView()
    }

    func setUpView() {
        if isRoundRectButton {
            self.layer.cornerRadius = self.bounds.height/2;
            self.clipsToBounds = true
        }
        else{
            self.layer.cornerRadius = self.cornerRadius;
            self.clipsToBounds = true
        }
    }

}

-1

Penso che questa sia la forma semplice

        Button1.layer.cornerRadius = 10(Half of the length and width)
        Button1.layer.borderWidth = 2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.