Як я можу зробити так, щоб кнопка мала закруглену межу в Swift?


232

Я будую додаток за допомогою swift в останній версії Xcode 6, і хотів би знати, як я можу змінити свою кнопку, щоб вона могла мати закруглену межу, яку я міг би коригувати сам, якщо потрібно. Як тільки це буде зроблено, як я можу змінити колір межі без додавання до неї фону? Іншими словами, я хочу трохи закруглену кнопку без фона, лише рамку 1pt певного кольору.

Відповіді:


528

Використовуйте button.layer.cornerRadius, button.layer.borderColorі button.layer.borderWidth. Зауважте, що borderColorпотрібен знак a CGColor, так що ви можете сказати (Swift 3/4):

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

2
Я спробував це, але у мене є невелика проблема, коли перший текст починається від самої кордону, тож не так добре виглядає, чи є спосіб вирішити це
vinbhai4u

2
@ vinbhai4u Спробуйте використовувати titleEdgeInsets.
повернення справжнього

як ми можемо зробити поле більшим за текст кнопки?
Раніше

створити розетку для кнопки для посилання на неї
Робота

я хочу використовувати його так, але не працювати :( UIButton.appearance (). layer.cornerRadius = 4
MR

65

Щоб виконати цю роботу в раскадровці (Інспектор побудови інтерфейсу)

За допомогою IBDesignableми можемо додати більше варіантів для Інспектора Інтерфейсу Builder UIButtonі налаштувати їх на розкадровці. Спочатку додайте наступний код до свого проекту.

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

Потім просто встановіть атрибути для кнопок на розкадровці.

введіть тут опис зображення


4
Це більш-менш копія відповіді нижче Хамза Газуані.
повернення правдиво

коли я використовую вищезазначене рішення для boarderColor, його збірка не вдалася в програмі інтерфейсів, я використовую наведений нижче код, але я успішний, якщо використовував didset, а не встановлював і отримував. @IBInspectable var borderColor: UIColor = .red {// didSet {// layer.borderColor = borderColor.cgColor //} встановити {guard let uiColor = newValue else {return} layer.borderColor = uiColor.cgColor} отримати {Guard let color = layer.borderColor else {return nil} повернути UIColor (cgColor: color)}}
Amr Angry

1
Нічого собі, як магія! Дякую! Googlers - переконайтеся, що цей фрагмент коду повністю розміщений поза вашою останньою фігурною дужкою у файлі, в який ви розміщуєте його - фрагмент коду не повинен міститися в будь-якому класі чи функції
velkoon

24

Я створив просту підкадру UIButton, яка використовує tintColorдля тексту текст і межі кольорів і, коли виділено, змінює свій фон на 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)
}
}

Для цього використовується розширення UIImage, яке створює зображення з кольору, я знайшов цей код тут: https://stackoverflow.com/a/33675160

Найкраще він працює, коли встановлено тип "Custom" в "Інтерфейсі", оскільки тип системи за замовчуванням трохи змінює кольори, коли кнопка підсвічується.


13

Цей клас заснований на всіх коментарях та пропозиціях у відповідях, а також їх можна позначити безпосередньо з xcode. Скопіюйте у свій проект і вставте будь-який UIButton та перейдіть до використання спеціального класу, тепер просто виберіть кольори межі чи фону з xcode для нормальних та / або виділених станів.

//
//  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
Чому імпортування Foundationпри імпорті UIKitдійсно достатньо?
повернення справжнього

@returntrue Так, ти маєш рацію, фундамент не потрібен, я взяв оригінальний базовий шаблон xcode, якщо я не помиляюся. Я оновив код.
le0diaz

Єдине, що це робить кнопку постійно круглою. Можливо, ви можете додати властивість радіуса кута, щоб також це виправити.
Andreas777

10

На основі @returntrue відповіді мені вдалося реалізувати це в Interface Builder.

Для отримання кнопки круглих кутів за допомогою Interface Builder додайте ключ Path = "layer.cornerRadius"із клавішею " " Type = "Number"та Value = "10"(або іншим необхідним значенням) у " User Defined RunTime Attribute" Identity Inspectorкнопки.


1
Це дійсно працює, але не є специфічним для Swift. Питання прямо задає питання, як досягти бажаного ефекту за допомогою Swift, а не за допомогою раскадровки тощо.
повернення справжнього

2
Моя відповідь була спрямована не до ОП, а до інших читачів, які зіткнулися з моєю проблемою. і лише знайшовши цю посаду, вдалося вирішити проблему. На мій погляд та досвід, відповіді не мають точно відповідати на те, що запитувала ОП, якщо вони пов'язані з цим та дають додаткову інформацію, корисну для читачів. Скидаючи такі відповіді, ви відмовляєте від корисних відповідей.
Zvi

1
Хоча це взагалі вірно - особливо для широких запитань - саме це питання є чітким за своїм характером. Існує безліч питань, які задають чітке рішення за допомогою розкадрівки, і, якщо можливо, читачі хотіли, щоб рішення, яке використовує розкадровку, для додавання цих запитів достатньо для додавання «розгортки» до пошукових термінів Google. Я не кажу, що ваша відповідь погана, але скоріше в неправильному місці.
повернення справжнього

Оскільки я визнав цю публікацію найбільш близькою до вирішення моєї проблеми, я розмістив її тут. Йти своїм шляхом означає, що я повинен був написати новий пост і відповісти на свій пост, який, як мені здається, трохи непосильний.
Zvi

1
Я знайшов ті питання, які, здається, вирішують вашу проблему краще, ніж це: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
повернення справжнього

6

Ви можете використовувати цей підклас UIButton для налаштування UIButton відповідно до ваших потреб.

відвідайте це рефіні github для ознайомлення

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

хто б не порушив це, ти навіть намагався працювати таким чином? Просто не витрачай свій час на зволікання
Jha

Чому потік? це, здається, працює і дуже чисто.
ColdGrub1384

@ ColdGrub1384 саме :)
Nikesh Jha

3

Я думаю, що найпростіший і найчистіший спосіб - це використовувати протокол, щоб уникнути повторення спадщини та коду. Ви можете змінити ці властивості безпосередньо з розкадрівки

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

Оновлення

У цьому посиланні ви можете знайти приклад з утилітою протоколу Traceable

введіть тут опис зображення


2
Тут вам не дуже потрібен протокол. Працює цілком чудово без цього.
повернення справжнього

2
Привіт @returntrue, я не знаю, чому ти спростуєш мою відповідь ... Я думаю, ти цього не зрозумів, запрошую вас переглянути цей сеанс: developer.apple.com/videos/play/wwdc2015/408 Навіщо використовувати протокол? Протокол забезпечує більшу гнучкість, я можу мати реалізацію за замовчуванням і т. д. Моя відповідь дозволяє редагувати ці властивості на дошці, і мені не потрібно повторювати код або підклас будь-якого класу. Ваша відповідь правильна, але тут я поділився іншим способом зробити це Ви заперечуєте всі інші відповіді, ви повинні пам’ятати, що Stackoverflow - це обмін нашими знаннями, перш ніж завоювати репутацію
Hamza

2
Правильно, ваш код дозволяє всі перераховані вами речі. Але, оскільки UIView є надкласом всіх елементів інтерфейсу, вашого розширення UIView достатньо, щоб виконати роботу. Протокол, який слід відстежувати, ні в якому разі не потребує вдосконалень (оскільки він просто перекладається на UIView - відстежуються лише UIView і його підкласи).
повернення справжнього

1
привіт @returntrue, ви маєте рацію в цьому випадку використання, можливо, нам не потрібен протокол, але з ним ми маємо більшу гнучкість, і, наприклад, ми можемо додавати значення за замовчуванням для застосування та доступні лише для UIImageView та UIButton, я оновлю мою відповідь на цьому прикладі :)
Хамза

1
Насправді не має сенсу застосовувати значення за замовчуванням для таких властивостей, як колір кутового радіуса або колір межі. Ці значення повинні бути застосовані до кожного екземпляра представлення у вашому інтерфейсі окремо, змістовно.
повернути справжнє

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

окрім підказки, переконайтеся, що ваша кнопка не є підкласом будь-якого користувальницького класу на дошці сюжетів; у такому випадку найкраще місце вашого коду має бути у користувацькому класі, оскільки сам код викликає роботу лише з користувацького класу, якщо кнопка є підкласом за замовчуванням Клас UIButton та розетка цього, сподіваюся, це може допомогти будь-кому замислитися, чому кутові радіоприймачі не застосовуються на моїй кнопці з коду.



0

Спробуй цю межу кнопки із закругленими кутами

anyButton.backgroundColor = .clear

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

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor

так, але це вводить ідею використання cornerRadius, який є динамічним, я не думаю, що це було просто скопійовано та вставлено.
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

Ви можете підклас UIButtonі додати @IBInspectableдо нього змінні, щоб ви могли налаштувати спеціальні параметри кнопки через StoryBoard "Ревізор атрибутів". Нижче я записую цей код.

@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

Я думаю, що це проста форма

        Button1.layer.cornerRadius = 10(Half of the length and width)
        Button1.layer.borderWidth = 2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.