Como faço para que um botão tenha uma borda arredondada no Swift?

232

Estou criando um aplicativo usando o swift na versão mais recente do Xcode 6 e gostaria de saber como posso modificar meu botão para que ele tenha uma borda arredondada e que eu possa me ajustar, se necessário. Feito isso, como posso alterar a cor da borda sem adicionar um plano de fundo? Em outras palavras, quero um botão ligeiramente arredondado, sem fundo, apenas uma borda de 1 ponto de uma determinada cor.

GuiGui23
fonte

Respostas:

528

Use button.layer.cornerRadius, button.layer.borderColore button.layer.borderWidth. Note que borderColorrequer a CGColor, então você pode dizer (Swift 3/4):

button.backgroundColor = .clear
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.black.cgColor
retornar verdadeiro
fonte
2
Eu tentei isso, mas eu tenho um pequeno problema o primeiro texto é a partir da própria fronteira, de modo que não é tão bom aspecto, há alguma maneira de resolver este
vinbhai4u
2
@ vinbhai4u Tente usar titleEdgeInsets.
return true
como podemos tornar a caixa maior que o texto do botão?
Cedo
criar uma saída para o botão para se referir a ele
Jobs
Eu quero usá-lo assim, mas não funciona :( UIButton.appearance (). layer.cornerRadius = 4
MR
65

Para fazer este trabalho no storyboard (Interface Builder Inspector)

Com a ajuda de IBDesignable, podemos adicionar mais opções ao Interface Builder Inspector UIButtone ajustá-las no storyboard. Primeiro, adicione o seguinte código ao seu projeto.

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

Em seguida, basta definir os atributos dos botões no storyboard.

insira a descrição da imagem aqui

Fangming
fonte
4
Esta é mais ou menos uma cópia da resposta abaixo de Hamza Ghazouani.
return true
Quando eu uso a solução acima para o boarderColor que ele construiu falhou no interfacebuilder, eu uso o código abaixo, mas obtive sucesso se eu usasse didset em vez de definir e obter. @IBInspectable var borderColor: UIColor = .red {// didSet {// layer.borderColor = borderColor.cgColor //} conjunto {guarda deixe uiColor = newValue else {retorno} layer.borderColor = uiColor.cgColor} obtenha {guarda deixe cor = layer.borderColor else {return nil} retorne UIColor (cgColor: color)}}
Amr Irritado
1
Uau, como mágica! Obrigado! Googlers - certifique-se de colocar esse snippet de código completamente fora da sua última chave no arquivo em que o está inserindo - o snippet de código não deve estar em nenhuma classe ou função
velkoon
24

Eu criei uma sublcass UIButton simples que usa as tintColorcores de texto e borda e, quando destacada, altera o plano de fundo para 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)
}
}

Isso faz uso de uma extensão UIImage que cria uma imagem de uma cor. Encontrei o código aqui: https://stackoverflow.com/a/33675160

Funciona melhor quando definido para digitar Personalizado no construtor de interface, pois o tipo de sistema padrão modifica ligeiramente as cores quando o botão é destacado.

sgib
fonte
13

Esta classe é baseada em todos os comentários e sugestões nas respostas e também pode ser projetada diretamente no xcode. Copie para o seu projeto e insira qualquer UIButton e altere para usar a classe personalizada, agora basta selecionar a borda ou a cor de fundo do xcode para estados normais e / ou destacados.

//
//  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
fonte
1
Por que importar Foundationao importar UIKitrealmente é suficiente?
return true
@returntrue Sim, você está certo, o Foundation não é necessário, eu vim do modelo básico original do xcode se não estiver errado. Eu atualizei o código.
Le0diaz
A única coisa é que isso fará o botão circular o tempo todo. Talvez você possa adicionar uma propriedade raio do canto para corrigir isso também.
Andreas777
10

Com base na resposta do @returntrue, consegui implementá-lo no Interface Builder.

Para obter o botão de cantos arredondados usando o Interface Builder, adicione uma chave Path = "layer.cornerRadius"com Type = "Number"e Value = "10"(ou outro valor conforme necessário) no " User Defined RunTime Attribute" do Identity Inspectorbotão.

Zvi
fonte
1
Isso realmente funciona, mas não é específico do Swift. A pergunta pergunta explicitamente como alcançar o efeito desejado usando Swift, não usando storyboard ou algo parecido.
return true
2
Minha resposta foi direcionada não ao OP, mas a outros leitores que enfrentaram o meu problema. e somente encontrando este post foi capaz de resolver o problema. Na minha opinião e experiência, as respostas não têm exatamente o que o OP pediu, desde que relacionadas a ele e forneçam informações extras que sejam úteis para os leitores. Ao votar para baixo essas respostas, você desencoraja respostas úteis.
Zvi
1
Embora isso geralmente seja verdade - especialmente para questões amplas -, essa questão é clara em seu caráter. Existe uma infinidade de perguntas que solicitam uma solução explícita via storyboard e, se possível, os leitores desejam ter uma solução que utilize o storyboard; em seguida, adicionar "storyboard" aos termos de pesquisa do Google é suficiente para mostrar essas perguntas. Não estou dizendo que sua resposta seja ruim, mas no lugar errado.
return true
Como achei esta postagem a mais próxima de resolver meu problema, postei aqui. Seguir o seu caminho significa que eu deveria ter escrito uma nova postagem e respondê-la, o que me parece um pouco exagerado.
Zvi
1
Encontrei as perguntas que parecem resolver o seu problema melhor do que esta: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
return true
6

Você pode usar esta subclasse de UIButton para personalizar o UIButton conforme suas necessidades.

visite este repositório do github para referência

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
fonte
quem downvoted isso, fez u mesmo tentar trabalhar desta forma ??? apenas não desperdice o seu tempo downvoting
Nikesh Jha
Por que um voto negativo? isso parece funcionar e é muito limpo.
precisa saber é o seguinte
@ ColdGrub1384 exatamente :)
Nikesh Jha
3

Eu acho que a maneira mais fácil e limpa de usar o protocolo é evitar a herança e a repetição de código. Você pode alterar essas propriedades diretamente do 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 }
    }
}

Atualizar

Neste link, você pode encontrar um exemplo com o utilitário do protocolo Rastreável

insira a descrição da imagem aqui

Hamza
fonte
2
Você realmente não precisa do protocolo aqui. Funciona completamente bem sem ele.
return true
2
Olá @returntrue, não sei por que você recusou minha resposta ... Acho que você não entendeu, convido você a assistir a esta sessão: developer.apple.com/videos/play/wwdc2015/408 Por que usar o protocolo? o protocolo permite mais flexibilidade, posso ter uma implementação padrão, etc. Minha resposta permite editar essas propriedades no storyboard e não preciso repetir o código ou subclassificar qualquer classe Sua resposta está correta, mas aqui eu compartilhei outra maneira de fazer -lo você downvote todas as outras respostas, você deve se lembrar que Stackoverflow é para compartilhar nossos conhecimentos antes de ganhar reputação
Hamza
2
Correto, seu código permite todas essas coisas que você listou. MAS, como UIView é a superclasse de todos os elementos da interface do usuário, sua extensão UIView é suficiente para fazer o trabalho. O protocolo Traceable não é necessário de forma alguma e não produz melhorias (como ele simplesmente se traduz em UIView - apenas UIViews e suas subclasses são rastreáveis).
return true
1
oi @returntrue, você está certo neste caso de uso, pode ser, não precisamos de protocolo, mas com ele temos mais flexibilidade e, por exemplo, podemos adicionar valores padrão a serem aplicados e disponíveis apenas para UIImageView e UIButton, I irá atualizar a minha resposta com este exemplo :)
Hamza
1
Realmente não faz sentido aplicar valores padrão a propriedades como raio do canto ou cor da borda. Esses valores devem ser aplicados a cada instância de exibição em sua interface do usuário separadamente, de maneira significativa.
return true
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
fonte
1

como dica à parte, certifique-se de que seu botão não seja subclasse de nenhuma classe personalizada no storyboard; nesse caso, o melhor lugar para seu código deve estar na classe personalizada, pois o código funciona apenas fora da classe personalizada se o botão for subclasse do padrão Classe UIButton e saída, espero que isso ajude alguém a se perguntar por que os rádios de canto não se aplicam ao meu botão do código.

IsPha
fonte
0
@IBOutlet weak var button: UIButton!

...

Eu acho que por raio apenas este parâmetro:

button.layer.cornerRadius = 5
Iryna Batvina
fonte
Parece que isso não adiciona nada de novo.
return true
0

Experimente esta borda de botão com cantos arredondados

anyButton.backgroundColor = .clear

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

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor
Raghib Arshi
fonte
sim, mas introduz a ideia de usar um cornerRadius que é dinâmico, não acho que foi simplesmente copiado e colado.
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
fonte
-1

Você pode criar subclasses UIButtone adicionar @IBInspectablevariáveis ​​a ele para poder configurar os parâmetros do botão personalizado através do StoryBoard "Attribute Inspector". Abaixo, escrevo esse 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
fonte
-1

Eu acho que essa é a forma simples

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