¿Cómo puedo hacer que un botón tenga un borde redondeado en Swift?

232

Estoy creando una aplicación usando swift en la última versión de Xcode 6, y me gustaría saber cómo puedo modificar mi botón para que pueda tener un borde redondeado que pueda ajustar yo mismo si es necesario. Una vez hecho esto, ¿cómo puedo cambiar el color del borde sin agregarle un fondo? En otras palabras, quiero un botón ligeramente redondeado sin fondo, solo un borde de 1pt de cierto color.

GuiGui23
fuente

Respuestas:

528

Utilizar button.layer.cornerRadius, button.layer.borderColory button.layer.borderWidth. Tenga en cuenta que borderColorrequiere un CGColor, por lo que podría decir (Swift 3/4):

button.backgroundColor = .clear
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.black.cgColor
volver verdadero
fuente
2
Intenté esto, pero tengo un pequeño problema: el primer texto comienza desde la frontera misma, por lo que no es tan atractivo, ¿hay alguna manera de resolver esto
Vinbhai4u
2
@ vinbhai4u Intenta usar titleEdgeInsets.
Vuelve el verdadero
¿Cómo podemos hacer que el cuadro sea más grande que el texto del botón?
Antes del
crear una salida para que el botón haga referencia a ella
Jobs
Quiero usarlo así, pero no funciona :( UIButton.appearance (). layer.cornerRadius = 4
MR
65

Para hacer este trabajo en storyboard (Inspector del generador de interfaces)

Con la ayuda de IBDesignable, podemos agregar más opciones a Interface Builder Inspector UIButtony ajustarlas en el guión gráfico. Primero, agregue el siguiente código a su proyecto.

@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)
        }
    }
}

Luego, simplemente configure los atributos para los botones en el guión gráfico.

ingrese la descripción de la imagen aquí

Colmillo
fuente
44
Esta es más o menos una copia de la respuesta a continuación de Hamza Ghazouani.
Vuelve el verdadero
cuando uso la solución anterior para el boarderColor que falló su construcción en el creador de interfaces, uso el siguiente código, pero tengo éxito si utilizo didset en lugar de set and get. @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, como magia! ¡Gracias! Googlers: asegúrese de colocar este fragmento de código completamente fuera de su última llave en el archivo en el que lo está colocando: el fragmento de código no debe estar dentro de ninguna clase o función
velkoon
24

He creado una subclase simple UIButton que usa el tintColortexto y los colores de los bordes y, cuando está resaltado, cambia su fondo a 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)
}
}

Esto hace uso de una extensión UIImage que crea una imagen a partir de un color. Encontré ese código aquí: https://stackoverflow.com/a/33675160

Funciona mejor cuando se configura para escribir Personalizado en el generador de interfaces, ya que el tipo de sistema predeterminado modifica ligeramente los colores cuando se resalta el botón.

sgib
fuente
13

Esta clase se basa en todos los comentarios y sugerencias en las respuestas, y también se puede designar directamente desde xcode. Copie a su proyecto e inserte cualquier UIButton y cambie para usar una clase personalizada, ahora solo seleccione el color del borde o del fondo de xcode para los estados normales y / o resaltados.

//
//  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
    }
}
le0diaz
fuente
1
¿Por qué importar Foundationcuando importar es UIKitrealmente suficiente?
retorno verdadera
@returntrue Sí, tienes razón, no se requiere Foundation, vengo de la plantilla básica original de xcode si no me equivoco. Actualicé el código.
le0diaz
Lo único es que esto hará que el botón sea circular todo el tiempo. Quizás también pueda agregar una propiedad de radio de esquina para arreglar esto.
Andreas777
10

Basado en la respuesta @returntrue, logré implementarlo en Interface Builder.

Para obtener el botón de esquinas redondeadas con Interface Builder, agregue una Clave Path = "layer.cornerRadius"con Type = "Number"y Value = "10"(u otro valor según sea necesario) en el " User Defined RunTime Attribute" del Identity Inspectorbotón.

Zvi
fuente
1
De hecho, esto funciona, pero no es específico de Swift. La pregunta pregunta explícitamente cómo lograr el efecto deseado usando Swift, no usando storyboard o similar.
Vuelve el verdadero
2
Mi respuesta fue dirigida no al OP sino a otros lectores que enfrentaron mi problema. y solo al encontrar esta publicación pudimos resolver el problema. En mi opinión y experiencia, las respuestas no tienen que ser exactamente lo que pidió el OP, siempre y cuando estén relacionadas con él y brinden información adicional que sea útil para los lectores. Al rechazar dichas respuestas, desalienta las respuestas útiles.
Zvi
1
Si bien esto generalmente es cierto, especialmente para preguntas amplias, esta misma pregunta es clara en su carácter. Existe una multitud de preguntas que solicitan una solución explícita a través del guión gráfico, y si es posible los lectores querían tener una solución que utilice el guión gráfico, basta con agregar "guión gráfico" a los términos de búsqueda de Google para mostrar estas preguntas. No digo que su respuesta sea mala, sino que está en el lugar equivocado.
Vuelve el verdadero
Como encontré que esta publicación es la más cercana a abordar mi problema, la publiqué aquí. Seguir tu camino significa que debería haber escrito una nueva publicación y responder mi publicación, lo que me parece un poco exagerado.
Zvi
1
He encontrado esas preguntas que parecen abordar su problema mejor que esta: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
devuelve verdadero
6

Puede usar esta subclase de UIButton para personalizar UIButton según sus necesidades.

visite este repositorio de github para referencia

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()
    }
}
Nikesh Jha
fuente
quien haya votado en contra de esto, ¿incluso trataste de trabajar de esta manera? Simplemente no pierdas tu tiempo en votar por votación negativa
Nikesh Jha
¿Por qué un voto negativo? Esto parece funcionar y está muy limpio.
ColdGrub1384
@ ColdGrub1384 exactamente :)
Nikesh Jha
3

Creo que la forma más fácil y limpia es utilizar el protocolo para evitar la repetición de la herencia y el código. Puede cambiar estas propiedades directamente desde el guión gráfico

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 }
    }
}

Actualizar

En este enlace puede encontrar un ejemplo con la utilidad del protocolo Trazable

ingrese la descripción de la imagen aquí

Hamza
fuente
2
Realmente no necesitas el protocolo aquí. Funciona completamente bien sin él.
Vuelve el verdadero
2
Hola @returntrue, no sé por qué rechazaste mi respuesta ... Creo que no la entendiste, te invito a ver esta sesión: developer.apple.com/videos/play/wwdc2015/408 ¿Por qué usar el protocolo? el protocolo permite más flexibilidad, puedo tener una implementación predeterminada, etc. Mi respuesta permite editar estas propiedades en el guión gráfico, y no necesito repetir el código o subclasificar ninguna clase. Su respuesta es correcta, pero aquí compartí otra forma de hacerlo. Si rechaza todas las demás respuestas, debe recordar que Stackoverflow es para compartir nuestros conocimientos antes de ganar reputación
Hamza
2
Correcto, su código permite todas estas cosas que enumeró. PERO, como UIView es la superclase de todos los elementos de la interfaz de usuario, su extensión UIView es suficiente para hacer el trabajo. El protocolo Rastreable no se requiere de ninguna manera y no produce ninguna mejora (ya que simplemente se traduce en UIView, solo UIViews y sus subclases son rastreables).
Vuelve el verdadero
1
hola @returntrue, tienes razón en este caso de uso, puede que no necesitemos protocolo, pero con él tenemos más flexibilidad y, por ejemplo, podemos agregar valores predeterminados para aplicar y solo están disponibles para UIImageView y UIButton, I actualizará mi respuesta con este ejemplo :)
Hamza
1
Realmente no tiene sentido aplicar valores predeterminados para propiedades como el radio de la esquina o el color del borde. Esos valores deben aplicarse a cada instancia de vista en su interfaz de usuario por separado de manera significativa.
Vuelve el verdadero
3
   @IBOutlet weak var yourButton: UIButton! {
        didSet{
            yourButton.backgroundColor = .clear
            yourButton.layer.cornerRadius = 10
            yourButton.layer.borderWidth = 2
            yourButton.layer.borderColor = UIColor.white.cgColor
        }
    }
Giang
fuente
1

como consejo adicional, asegúrese de que su botón no sea una subclase de ninguna clase personalizada en el guión gráfico, en tal caso, el mejor lugar de su código debe estar en la clase personalizada, ya que el código funciona solo fuera de la clase personalizada si su botón es una subclase del valor predeterminado UIButton clase y salida de la misma, espero que esto pueda ayudar a alguien se pregunta por qué las radios de esquina no se aplican en mi botón del código.

IsPha
fuente
0
@IBOutlet weak var button: UIButton!

...

Creo que para el radio lo suficiente este parámetro:

button.layer.cornerRadius = 5
Iryna Batvina
fuente
Esto no agrega nada nuevo, parece.
devuelve el verdadero
0

PRUEBE ESTE borde del botón con esquinas redondeadas

anyButton.backgroundColor = .clear

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

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor
Raghib Arshi
fuente
sí, pero introduce la idea de usar un cornerRadius que sea dinámico, no creo que simplemente se haya copiado y pegado.
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
        
    }
    
    
}
Azade Rahmati
fuente
-1

Puede subclasificar UIButtony agregarle @IBInspectablevariables para que pueda configurar los parámetros del botón personalizado a través del "Inspector de atributos" de StoryBoard. A continuación escribo ese código.

@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
        }
    }

}
Bhavesh Patel
fuente
-1

Creo que esta es la forma simple.

        Button1.layer.cornerRadius = 10(Half of the length and width)
        Button1.layer.borderWidth = 2
Graycodder
fuente