Wie kann ich dafür sorgen, dass eine Schaltfläche in Swift einen abgerundeten Rand hat?

232

Ich erstelle eine App mit swift in der neuesten Version von Xcode 6 und möchte wissen, wie ich meine Schaltfläche so ändern kann, dass sie einen abgerundeten Rand hat, den ich bei Bedarf selbst anpassen kann. Wie kann ich dann die Farbe des Rahmens selbst ändern, ohne einen Hintergrund hinzuzufügen? Mit anderen Worten, ich möchte eine leicht abgerundete Schaltfläche ohne Hintergrund, nur einen 1pt-Rand einer bestimmten Farbe.

GuiGui23
quelle

Antworten:

528

Verwenden Sie button.layer.cornerRadius, button.layer.borderColorund button.layer.borderWidth. Beachten Sie, dass ein a borderColorerforderlich ist CGColor, also könnte man sagen (Swift 3/4):

button.backgroundColor = .clear
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.black.cgColor
return true
quelle
2
Ich habe es versucht, aber ich habe ein kleines Problem, der erste Text beginnt an der Grenze selbst, daher ist es nicht so gut aussehend, dass es eine Möglichkeit gibt, dies zu lösen
vinbhai4u
2
@ vinbhai4u Versuchen Sie es mit titleEdgeInsets.
Rückgabe wahr
Wie können wir das Feld größer als den Schaltflächentext machen?
Früher
Erstellen Sie eine Steckdose für die Schaltfläche, um darauf zu verweisen
Jobs
Ich möchte es so verwenden, aber nicht funktionieren :( UIButton.appearance (). layer.cornerRadius = 4
MR
65

Um diesen Job im Storyboard zu erledigen (Interface Builder Inspector)

Mithilfe von IBDesignablekönnen wir dem Interface Builder Inspector weitere Optionen hinzufügen UIButtonund diese im Storyboard optimieren. Fügen Sie Ihrem Projekt zunächst den folgenden Code hinzu.

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

Legen Sie dann einfach die Attribute für Schaltflächen im Storyboard fest.

Geben Sie hier die Bildbeschreibung ein

Fangming
quelle
4
Dies ist mehr oder weniger eine Kopie der Antwort von Hamza Ghazouani.
Rückgabe wahr
Wenn ich die obige Lösung für die BoarderColor verwende, die im Interfacebuilder fehlgeschlagen ist, verwende ich den folgenden Code, aber ich habe Erfolg, wenn ich Didset verwendet habe, anstatt Set and Get. @IBInspectable var borderColor: UIColor = .red {// didSet {// layer.borderColor = borderColor.cgColor //} set {guard let uiColor = newValue sonst {return} layer.borderColor = uiColor.cgColor} get {guard let color = layer.borderColor else {return nil} return UIColor (cgColor: color)}}
Amr Angry
1
Wow, wie Magie! Danke dir! Googler - stellen Sie sicher, dass Sie dieses Code-Snippet vollständig außerhalb Ihrer allerletzten geschweiften Klammer in der Datei platzieren, in der Sie es
ablegen
24

Ich habe eine einfache UIButton-Unterklasse erstellt, die die tintColorfür ihre Text- und Rahmenfarben verwendet und bei Hervorhebung ihren Hintergrund in die ändert 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)
}
}

Dies verwendet eine UIImage-Erweiterung, die ein Bild aus einer Farbe erstellt. Ich habe diesen Code hier gefunden: https://stackoverflow.com/a/33675160

Dies funktioniert am besten, wenn Sie im Interface Builder die Option Benutzerdefiniert eingeben, da der Standardsystemtyp die Farben geringfügig ändert, wenn die Schaltfläche hervorgehoben wird.

sgib
quelle
13

Diese Klasse basiert auf allen Kommentaren und Vorschlägen in den Antworten und kann auch direkt aus xcode gestaltet werden. Kopieren Sie in Ihr Projekt, fügen Sie einen beliebigen UIButton ein und ändern Sie ihn, um eine benutzerdefinierte Klasse zu verwenden. Wählen Sie nun einfach die Rahmen- oder Hintergrundfarbe aus dem xcode für normale und / oder hervorgehobene Zustände aus.

//
//  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
quelle
1
Warum reicht der Import Foundationbeim Import UIKitwirklich aus?
Rückgabe wahr
@returntrue Ja, Sie haben Recht, Foundation ist nicht erforderlich. Ich habe die ursprüngliche xcode-Basisvorlage verwendet, wenn ich mich nicht irre. Ich habe den Code aktualisiert.
Le0diaz
Das einzige ist, dass dadurch die Schaltfläche die ganze Zeit kreisförmig wird. Möglicherweise können Sie auch eine Eckradius-Eigenschaft hinzufügen, um dies zu beheben.
Andreas777
10

Basierend auf der Antwort von @returntrue konnte ich sie in Interface Builder implementieren.

Um mit dem Interface Builder eine Schaltfläche für runde Ecken zu erhalten, fügen Sie einen Schlüssel Path = "layer.cornerRadius"mit Type = "Number"und Value = "10"(oder einem anderen erforderlichen Wert) in das " User Defined RunTime Attribute" Identity Inspectorder Schaltfläche ein.

Zvi
quelle
1
Dies funktioniert zwar, ist aber nicht Swift-spezifisch. In der Frage wird explizit gefragt, wie der gewünschte Effekt mit Swift erzielt werden kann, nicht mit Storyboard oder Ähnlichem.
Rückkehr wahr
2
Meine Antwort richtete sich nicht an das OP, sondern an andere Leser, die mit meinem Problem konfrontiert waren. und nur durch das Finden dieses Beitrags konnten wir das Problem lösen. Meiner Meinung nach und meiner Erfahrung nach müssen die Antworten nicht genau den Anforderungen des OP entsprechen, sofern sie damit zusammenhängen und zusätzliche Informationen enthalten, die für die Leser nützlich sind. Wenn Sie solche Antworten ablehnen, entmutigen Sie nützliche Antworten.
Zvi
1
Während dies im Allgemeinen zutrifft - insbesondere für allgemeine Fragen -, ist genau diese Frage in ihrem Charakter klar. Es gibt eine Vielzahl von Fragen, die eine explizite Lösung über das Storyboard erfordern. Wenn möglich, möchten die Leser eine Lösung, die das Storyboard verwendet, und das Hinzufügen von "Storyboard" zu den Google-Suchbegriffen reicht aus, um diese Fragen anzuzeigen. Ich sage nicht, dass Ihre Antwort schlecht ist, sondern am falschen Ort.
Rückgabe wahr
Da ich festgestellt habe, dass dieser Beitrag meinem Problem am nächsten kommt, habe ich ihn hier veröffentlicht. Wenn Sie Ihren Weg gehen, hätte ich einen neuen Beitrag schreiben und meinen Beitrag beantworten sollen, was mir ein wenig übertrieben erscheint.
Zvi
1
Ich habe die Fragen gefunden, die Ihr Problem besser zu lösen scheinen als diese: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
return true
6

Mit dieser Unterklasse von UIButton können Sie UIButton an Ihre Bedürfnisse anpassen.

Besuchen Sie dieses Github-Repo als Referenz

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
quelle
Wer auch immer dies abgelehnt hat, hat sogar versucht, auf diese Weise zu arbeiten? Verschwenden Sie einfach nicht Ihre Zeit mit Abstimmungen
Nikesh Jha
Warum eine Ablehnung? Das scheint zu funktionieren und ist sehr sauber.
ColdGrub1384
@ ColdGrub1384 genau :)
Nikesh Jha
3

Ich denke, der einfachste und sauberste Weg ist die Verwendung eines Protokolls, um Vererbung und Codewiederholung zu vermeiden. Sie können diese Eigenschaften direkt im Storyboard ändern

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

Aktualisieren

In diesem Link finden Sie ein Beispiel mit dem Dienstprogramm Traceable Protocol

Geben Sie hier die Bildbeschreibung ein

Hamza
quelle
2
Sie brauchen das Protokoll hier nicht wirklich. Funktioniert ganz gut ohne.
Rückkehr wahr
2
Hallo @returntrue, ich weiß nicht, warum Sie meine Antwort abgelehnt haben ... Ich glaube, Sie haben sie nicht verstanden. Ich lade Sie ein, diese Sitzung anzusehen: developer.apple.com/videos/play/wwdc2015/408 Warum Protokoll verwenden? Das Protokoll bietet mehr Flexibilität, ich kann eine Standardimplementierung durchführen usw. Meine Antwort ermöglicht das Bearbeiten dieser Eigenschaften im Storyboard, und ich muss den Code nicht wiederholen oder eine Klasse unterordnen. Ihre Antwort ist korrekt, aber hier habe ich eine andere Möglichkeit beschrieben Wenn Sie alle anderen Antworten ablehnen, sollten Sie sich daran erinnern, dass Stackoverflow dazu dient, unser Wissen zu teilen, bevor Sie sich einen Ruf verdienen
Hamza
2
Richtig, Ihr Code erlaubt all diese Dinge, die Sie aufgelistet haben. ABER da UIView die Oberklasse aller UI-Elemente ist, reicht Ihre UIView-Erweiterung aus, um die Aufgabe zu erledigen. Das Protokoll Traceable ist in keiner Weise erforderlich und führt zu keinen Verbesserungen (da es einfach in UIView übersetzt wird - nur UIViews und seine Unterklassen sind rückverfolgbar).
Rückkehr wahr
1
hi @returntrue, Sie haben in diesem Anwendungsfall Recht, vielleicht brauchen wir kein Protokoll, aber damit haben wir mehr Flexibilität und können beispielsweise Standardwerte hinzufügen, die angewendet werden sollen und nur für UIImageView und UIButton I verfügbar sind wird meine Antwort mit diesem Beispiel aktualisieren :)
Hamza
1
Es ist nicht wirklich sinnvoll, Standardwerte für Eigenschaften wie Eckenradius oder Rahmenfarbe anzuwenden. Diese Werte sollten auf sinnvolle Weise separat auf jede Ansichtsinstanz in Ihrer Benutzeroberfläche angewendet werden.
Rückgabe wahr
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
quelle
1

Stellen Sie neben dem Tipp sicher, dass Ihre Schaltfläche keiner Unterklasse einer benutzerdefinierten Klasse im Storyboard angehört. In diesem Fall sollte Ihr Code am besten in der benutzerdefinierten Klasse platziert werden. Dies liegt daran, dass Code nur außerhalb der benutzerdefinierten Klasse funktioniert, wenn Ihre Schaltfläche eine Unterklasse der Standardklasse ist UIButton-Klasse und Ausgang davon, hoffe, dies kann jedem helfen, sich zu fragen, warum Eckradios nicht auf meinen Button aus Code angewendet werden.

IsPha
quelle
0
@IBOutlet weak var button: UIButton!

...

Ich denke für Radius gerade genug diesen Parameter:

button.layer.cornerRadius = 5
Iryna Batvina
quelle
Dies fügt anscheinend nichts Neues hinzu.
Rückkehr wahr
0

VERSUCHEN SIE DIESEN Knopfrand mit abgerundeten Ecken

anyButton.backgroundColor = .clear

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

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor
Raghib Arshi
quelle
Ja, aber es führt die Idee ein, einen dynamischen CornerRadius zu verwenden. Ich glaube nicht, dass er einfach kopiert und eingefügt wurde.
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
quelle
-1

Sie können Unterklassen erstellen UIButtonund @IBInspectableVariablen hinzufügen , um die benutzerdefinierten Schaltflächenparameter über das StoryBoard "Attribute Inspector" zu konfigurieren. Unten schreibe ich diesen Code auf.

@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
quelle
-1

Ich denke, das ist die einfache Form

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