Как сделать так, чтобы кнопка в Swift имела закругленную границу?

232

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

GuiGui23
источник

Ответы:

528

Используйте button.layer.cornerRadius, button.layer.borderColorи button.layer.borderWidth. Обратите внимание, что borderColorтребуется 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мы можем добавить дополнительные параметры в Инспектор Интерфейсного Разработчика 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)
        }
    }
}

Затем просто установите атрибуты для кнопок на раскадровке.

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

Fangming
источник
4
Это более или менее копия ответа Хамзы Газуани ниже.
верни истину
Когда я использую вышеупомянутое решение для boarderColor, который не удалось построить в interface Builder, я использую приведенный ниже код, но я добьюсь успеха, если я использовал didset, а не set и 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
Вау, как волшебство! Спасибо! 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

Лучше всего он работает, когда в конструкторе интерфейса задано значение «Пользовательский», поскольку тип системы по умолчанию слегка изменяет цвета при выделении кнопки.

sgib
источник
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
    }
}
le0diaz
источник
1
Почему импорта Foundationпри импорте UIKitдействительно достаточно?
верни
@returntrue Да, вы правы, Foundation не требуется, я пришел из исходного базового шаблона xcode, если не ошибаюсь. Я обновил код.
le0diaz
Единственное, что это сделает кнопку круглой все время. Может быть, вы можете добавить свойство углового радиуса, чтобы исправить это тоже.
Andreas777
10

На основании ответа @returntrue мне удалось реализовать его в Интерфейсном Разработчике.

Чтобы получить кнопку с закругленными углами с помощью Интерфейсного Разработчика, добавьте Ключ Path = "layer.cornerRadius"с Type = "Number"и Value = "10"(или другим необходимым значением) в " User Defined RunTime Attribute" Identity Inspectorкнопки.

Цви
источник
1
Это действительно работает, но не специфично для Swift. Вопрос прямо спрашивает, как достичь желаемого эффекта с помощью Swift, а не с помощью раскадровки или тому подобного.
верни истину
2
Мой ответ был направлен не ОП, а другим читателям, которые столкнулись с моей проблемой. и только найдя этот пост смогли решить проблему. По моему мнению и опыту, ответы не обязательно должны быть точно такими, какие задал ФП, поскольку они связаны с ним и дают дополнительную информацию, полезную для читателей. Голосуя за такие ответы, вы отказываетесь от полезных ответов.
Цви
1
Хотя это в целом верно, особенно в отношении широких вопросов, этот вопрос ясен по своему характеру. Существует множество вопросов, которые задают явное решение с помощью раскадровки, и, если возможно, читатели хотели иметь решение, использующее раскадровку, тогда добавление «раскадровки» к поисковым терминам Google достаточно для отображения этих вопросов. Я не говорю, что ваш ответ плохой, а скорее не в том месте.
верните истину
Поскольку я нашел этот пост наиболее близким к решению моей проблемы, я разместил его здесь. Идти по своему пути означает, что я должен был написать новый пост и ответить на свой пост, который мне кажется немного излишним.
Цви
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()
    }
}
Никеш Джа
источник
Кто бы это ни отрицал, ты даже пытался работать таким образом? Просто не теряй времени на голосование
Никеш Джа
Почему отрицательный голос? Кажется, это работает и очень чисто.
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 protocol

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

Хамза
источник
2
Вам не нужен протокол здесь. Работает совершенно нормально без него.
верни
2
Привет @returntrue, я не знаю, почему ты опроверг мой ответ ... Я думаю, что ты не понял его, я приглашаю тебя посмотреть этот сеанс: developer.apple.com/videos/play/wwdc2015/408 Зачем использовать протокол? протокол обеспечивает большую гибкость, у меня может быть реализация по умолчанию и т. д. Мой ответ позволяет редактировать эти свойства в раскадровке, и мне не нужно повторять код или создавать подкласс любого класса. Ваш ответ правильный, но здесь я поделился другим способом сделать Если вы отрицаете все остальные ответы, вы должны помнить, что Stackoverflow предназначен для того, чтобы делиться своими знаниями, прежде чем зарабатывать репутацию
Хамза
2
Правильно, ваш код позволяет все эти вещи, которые вы перечислили. НО, поскольку UIView является суперклассом всех элементов пользовательского интерфейса, ваше расширение UIView достаточно для выполнения этой работы. Протокол Traceable не требуется никоим образом и не дает никаких улучшений (так как он просто переводит в UIView - отслеживаются только UIViews и его подклассы).
верни истину
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
@IBOutlet weak var button: UIButton!

...

Я думаю для радиуса как раз достаточно этого параметра:

button.layer.cornerRadius = 5
Ирина Батвина
источник
Это не добавляет ничего нового, кажется.
верните
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 «Attribute Inspector». Ниже я записываю этот код.

@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
Graycodder
источник