Jak sprawić, by przycisk miał zaokrągloną ramkę w Swift?

232

Buduję aplikację za pomocą swift w najnowszej wersji Xcode 6 i chciałbym wiedzieć, jak mogę zmodyfikować mój przycisk, aby miał zaokrągloną ramkę, którą w razie potrzeby mógłbym dostosować. Jak to zrobisz, jak mogę zmienić kolor samej ramki bez dodawania do niej tła? Innymi słowy, chcę lekko zaokrąglony przycisk bez tła, tylko 1-punktowa ramka określonego koloru.

GuiGui23
źródło

Odpowiedzi:

528

Używać button.layer.cornerRadius, button.layer.borderColori button.layer.borderWidth. Zauważ, że borderColorwymaga to CGColor, abyś mógł powiedzieć (Swift 3/4):

button.backgroundColor = .clear
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.black.cgColor
zwróć prawdę
źródło
2
Próbowałem tego, ale mam mały problem, pierwszy tekst zaczyna się od samej granicy, więc nie wygląda dobrze, czy jest jakiś sposób na rozwiązanie tego
vinbhai4u
2
@ vinbhai4u Spróbuj użyć titleEdgeInsets.
zwróć wartość rzeczywistą
w jaki sposób możemy uczynić pole większym niż tekst przycisku?
Wcześniej
utwórz ujście dla przycisku, aby mógł się do niego odwoływać
Jobs
chcę go używać w ten sposób, ale nie działa :( UIButton.appearance (). layer.cornerRadius = 4
MR
65

Aby wykonać tę pracę w scenorysie (Inspektor konstruktora interfejsów)

Za pomocą IBDesignablemożemy dodać więcej opcji do Inspektora konstruktora interfejsów UIButtoni dostosować je w serii ujęć. Najpierw dodaj następujący kod do swojego projektu.

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

Następnie po prostu ustaw atrybuty przycisków w serii ujęć.

wprowadź opis zdjęcia tutaj

Fangming
źródło
4
To mniej więcej kopia poniższej odpowiedzi Hamzy Ghazouani.
zwróć wartość rzeczywistą
kiedy używam powyższego rozwiązania dla boarderColor, kompilacja zakończyła się niepowodzeniem w programie budującym interfejsy, używam poniższego kodu, ale osiągam sukces, jeśli użyłem didset zamiast ustawiać i pobierać. @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, jak magia! Dziękuję Ci! Googlersi - upewnij się, że umieściłeś ten fragment kodu całkowicie poza ostatnim nawiasami klamrowymi w pliku, w którym go umieszczasz - fragment kodu nie powinien znajdować się w żadnej klasie ani funkcji
velkoon
24

Stworzyłem prostą sublcass UIButton, który używa tintColorkolorów tekstu i obramowania, a po podświetleniu zmienia tło na 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)
}
}

Wykorzystuje to rozszerzenie UIImage, które tworzy obraz z koloru, znalazłem ten kod tutaj: https://stackoverflow.com/a/33675160

Działa najlepiej, gdy w kreatorze interfejsów jest ustawiony typ Niestandardowy, ponieważ domyślny typ systemu nieznacznie modyfikuje kolory po podświetleniu przycisku.

sgib
źródło
13

Ta klasa oparta jest na wszystkich komentarzach i sugestiach w odpowiedziach, a także może być konfigurowalna bezpośrednio z xcode. Skopiuj do projektu i wstaw dowolny UIButton i zmień, aby użyć niestandardowej klasy, teraz po prostu wybierz kolor obramowania lub tła z xcode dla stanów normalnych i / lub podświetlonych.

//
//  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
źródło
1
Dlaczego importowanie Foundationpodczas importowania UIKitnaprawdę wystarczy?
zwróć wartość rzeczywistą
@returntrue Tak masz rację, Fundacja nie jest wymagana, pochodzę z oryginalnego podstawowego szablonu xcode, jeśli się nie mylę. Zaktualizowałem kod.
le0diaz
Chodzi tylko o to, że przycisk będzie cały czas okrągły. Być może możesz również dodać właściwość promienia narożnika, aby to naprawić.
Andreas777,
10

Na podstawie odpowiedzi @returntrue udało mi się zaimplementować ją w Konstruktorze interfejsów.

Aby zaokrąglić rogi przycisk za pomocą Konstruktora interfejsów, dodaj klucz Path = "layer.cornerRadius"z Type = "Number"i Value = "10"(lub inną wymaganą wartością) w „ User Defined RunTime AttributeIdentity Inspectorprzycisku.

Zvi
źródło
1
To rzeczywiście działa, ale nie jest specyficzne dla Swift. Pytanie wyraźnie pyta, jak osiągnąć pożądany efekt za pomocą Swift, a nie za pomocą scenorysu itp.
zwróć wartość rzeczywistą
2
Moja odpowiedź była skierowana nie do OP, ale do innych czytelników, którzy napotkali mój problem. i tylko przez znalezienie tego posta udało się rozwiązać problem. Moim zdaniem i doświadczenia odpowiedzi nie muszą dokładnie odpowiadać pytaniom PO, o ile są z tym związane i zawierają dodatkowe informacje przydatne dla czytelników. Głosując w dół na takie odpowiedzi, odradzasz przydatne odpowiedzi.
Zvi
1
Chociaż ogólnie jest to prawdą - szczególnie w przypadku szerokich pytań - to samo pytanie ma wyraźny charakter. Istnieje wiele pytań, które wymagają jednoznacznego rozwiązania za pomocą scenorysu, a jeśli to możliwe, czytelnicy chcieli mieć rozwiązanie, które wykorzystuje scenorys, a następnie dodanie „storyboardu” do wyszukiwanych haseł Google wystarczy, aby wyświetlić te pytania. Nie twierdzę, że twoja odpowiedź jest zła, ale raczej w złym miejscu.
zwróć true
Ponieważ uznałem ten post za najbliższy do rozwiązania mojego problemu, opublikowałem go tutaj. Twoja droga oznacza, że ​​powinienem napisać nowy post i odpowiedzieć na mój post, co wydaje mi się nieco przesadne.
Zvi
1
Znalazłem te pytania, które wydają się lepiej rozwiązać Twój problem niż ten: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
zwraca wartość true
6

Możesz użyć tej podklasy UIButton, aby dostosować UIButton do swoich potrzeb.

odwiedź to repozytorium github w celach informacyjnych

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
źródło
ktokolwiek to ocenił, czy nawet próbowałeś działać w ten sposób ??? po prostu nie marnuj czasu na przegłosowywanie
Nikesh Jha,
Dlaczego głosowanie negatywne? to wydaje się działać i jest bardzo czyste.
ColdGrub1384
@ ColdGrub1384 dokładnie :)
Nikesh Jha
3

Myślę, że najłatwiejszym i najczystszym sposobem jest użycie protokołu, aby uniknąć dziedziczenia i powtarzania kodu. Możesz zmienić te właściwości bezpośrednio z scenorysu

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

Aktualizacja

W tym linku można znaleźć przykład z narzędziem Traceable Protocol

wprowadź opis zdjęcia tutaj

Hamza
źródło
2
Tak naprawdę nie potrzebujesz tutaj protokołu. Bez tego działa całkowicie dobrze.
zwróć wartość rzeczywistą
2
Cześć @returntrue, nie wiem, dlaczego głosujesz za moją odpowiedzią ... Myślę, że jej nie rozumiesz, zapraszam do obejrzenia tej sesji: developer.apple.com/videos/play/wwdc2015/408 Po co używać protokołu? protokół pozwala na większą elastyczność, mogę mieć domyślną implementację itp. Moja odpowiedź pozwala edytować te właściwości w serii ujęć i nie muszę powtarzać kodu ani podklasy żadnej klasy Twoja odpowiedź jest poprawna, ale tutaj podzieliłem się innym sposobem głosujesz za wszystkimi innymi odpowiedziami, pamiętaj, że Stackoverflow służy dzieleniu się naszą wiedzą przed zdobyciem reputacji
Hamza
2
Prawidłowo, twój kod dopuszcza wszystkie wymienione rzeczy. ALE, ponieważ UIView jest nadklasą wszystkich elementów interfejsu użytkownika, rozszerzenie UIView wystarcza do wykonania zadania. Protokół Monitorowanie nie jest w żaden sposób wymagany i nie wprowadza żadnych ulepszeń (ponieważ po prostu przekłada się na UIView - możliwe jest jedynie śledzenie UIView i jego podklas).
zwróć prawdę
1
cześć @returntrue, masz rację w tym przypadku użycia, być może nie potrzebujemy protokołu, ale dzięki temu mamy większą elastyczność, i na przykład możemy dodać wartości domyślne do zastosowania i dostępne tylko dla UIImageView i UIButton, I zaktualizuje moją odpowiedź tym przykładem :)
Hamza
1
Nie ma sensu stosowanie wartości domyślnych dla właściwości takich jak promień narożnika lub kolor obramowania. Wartości te należy zastosować do każdej instancji widoku w interfejsie użytkownika oddzielnie w znaczący sposób.
zwróć prawdę
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
źródło
1

na marginesie, upewnij się, że twój przycisk nie jest podklasą żadnej niestandardowej klasy w scenariuszu, w takim przypadku najlepszym miejscem w kodzie powinna być klasa niestandardowa, ponieważ kod własny działa tylko z niestandardowej klasy, jeśli twój przycisk jest podklasą domyślną Klasa UIButton i jej wylot, mam nadzieję, że może to pomóc każdemu zastanawiać się, dlaczego radiotelefony narożne nie działają na mój przycisk z kodu.

IsPha
źródło
0
@IBOutlet weak var button: UIButton!

...

Myślę, że dla promienia wystarczy ten parametr:

button.layer.cornerRadius = 5
Iryna Batvina
źródło
Wydaje się, że to nie dodaje nic nowego.
zwróć prawdziwy
0

SPRÓBUJ TEN obramowanie przycisku z zaokrąglonymi narożnikami

anyButton.backgroundColor = .clear

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

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor
Raghib Arshi
źródło
tak, ale wprowadza pomysł użycia dynamicznego narożnika Radius, nie sądzę, że został po prostu skopiowany i wklejony.
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
źródło
-1

Możesz podklasować UIButtoni dodawać @IBInspectabledo niego zmienne, dzięki czemu możesz konfigurować parametry przycisku niestandardowego za pomocą StoryBoard „Inspektora atrybutów”. Poniżej zapisuję ten kod.

@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
źródło
-1

Myślę, że to prosta forma

        Button1.layer.cornerRadius = 10(Half of the length and width)
        Button1.layer.borderWidth = 2
Graycodder
źródło