Bagaimana saya bisa membuat tombol memiliki batas bulat di Swift?

232

Saya sedang membangun aplikasi menggunakan cepat di versi terbaru Xcode 6, dan ingin tahu bagaimana saya bisa memodifikasi tombol saya sehingga dapat memiliki batas bulat yang dapat saya sesuaikan sendiri jika diperlukan. Setelah selesai, bagaimana saya bisa mengubah warna perbatasan itu sendiri tanpa menambahkan latar belakang? Dengan kata lain saya ingin tombol agak membulat tanpa latar belakang, hanya batas 1pt dengan warna tertentu.

GuiGui23
sumber

Jawaban:

528

Gunakan button.layer.cornerRadius, button.layer.borderColordan button.layer.borderWidth. Catatan yang borderColormembutuhkan CGColor, jadi Anda bisa mengatakan (Swift 3/4):

button.backgroundColor = .clear
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.black.cgColor
kembali benar
sumber
2
Saya mencoba ini, tapi saya punya masalah kecil, teks pertama dimulai dari perbatasan itu sendiri, jadi tidak terlalu bagus apakah ada cara untuk menyelesaikan ini
vinbhai4u
2
@ vinbhai4u Coba gunakan titleEdgeInsets.
mengembalikan true
bagaimana kita bisa membuat kotak lebih besar dari teks tombol?
Lebih cepat
buat saluran keluar untuk tombol untuk merujuknya
Jobs
saya ingin menggunakannya seperti ini tetapi tidak berfungsi :( UIButton.appearance (). layer.cornerRadius = 4
MR
65

Untuk melakukan pekerjaan ini di storyboard (Interface Builder Inspector)

Dengan bantuan IBDesignable, kami dapat menambahkan lebih banyak opsi ke Interface Builder Inspector untuk UIButtondan men-tweak mereka di storyboard. Pertama, tambahkan kode berikut ke proyek Anda.

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

Kemudian cukup atur atribut untuk tombol di storyboard.

masukkan deskripsi gambar di sini

Fangming
sumber
4
Ini kurang lebih merupakan salinan jawaban di bawah ini dari Hamza Ghazouani.
mengembalikan true
ketika saya menggunakan solusi di atas untuk boarderColor itu gagal dibangun di interfacebuilder, saya menggunakan kode di bawah ini tetapi saya berhasil jika saya menggunakan didset daripada mengatur dan mendapatkan. @IBInspectable var borderColor: UIColor = .red {// didSet {// layer.borderColor = borderColor.cgColor //} set {guard let uiColor = newValue else {kembali} layer.borderColor = uiColor.cgColor} dapatkan {guard let color = layer.borderColor else {return nil} return UIColor (cgColor: color)}}
Amr Angry
1
Wow, seperti sulap! Terima kasih! Googler - pastikan untuk menempatkan potongan kode ini di luar penjepit keriting Anda yang terakhir di file tempat Anda memasukkannya - potongan kode tidak boleh berada di dalam kelas atau fungsi apa pun
velkoon
24

Saya telah membuat sublcass UIButton sederhana yang menggunakan tintColor untuk teks dan warna perbatasan dan ketika disorot mengubah latar belakangnya ke 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)
}
}

Ini memanfaatkan ekstensi UIImage yang membuat gambar dari warna, saya menemukan kode itu di sini: https://stackoverflow.com/a/33675160

Ini bekerja paling baik ketika diatur untuk mengetikkan Custom in interface builder karena jenis Sistem default sedikit mengubah warna ketika tombol disorot.

sgib
sumber
13

Kelas ini didasarkan dari semua komentar dan saran dalam jawaban, dan juga dapat dirancang langsung dari xcode. Salin ke proyek Anda dan masukkan UIButton apa saja dan ubah untuk menggunakan kelas khusus, sekarang cukup pilih warna batas atau latar belakang dari xcode untuk kondisi normal dan / atau yang disorot.

//
//  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
sumber
1
Mengapa mengimpor Foundationketika mengimpor UIKitbenar-benar mencukupi?
mengembalikan true
@ returntrue Ya Anda benar, Foundation tidak diperlukan, saya berasal dari template dasar xcode asli jika saya tidak salah. Saya memperbarui kode.
le0diaz
Satu-satunya hal adalah ini akan membuat tombol melingkar sepanjang waktu. Mungkin Anda bisa menambahkan properti radius sudut untuk memperbaikinya juga.
Andreas777
10

Berdasarkan jawaban @ returntrue saya berhasil mengimplementasikannya di Interface Builder.

Untuk mendapatkan tombol sudut menggunakan Interface Builder tambahkan Kunci Path = "layer.cornerRadius"dengan Type = "Number"dan Value = "10"(atau nilai lain yang diperlukan) di " User Defined RunTime Attribute" Identity Inspectortombol.

Zvi
sumber
1
Ini memang berhasil, tetapi tidak spesifik untuk Swift. Pertanyaannya secara eksplisit menanyakan bagaimana mencapai efek yang diinginkan dengan menggunakan Swift, bukan dengan menggunakan storyboard atau sejenisnya.
mengembalikan true
2
Jawaban saya diarahkan bukan untuk OP tetapi untuk pembaca lain yang menghadapi masalah saya. dan hanya dengan menemukan pos ini dapat menyelesaikan masalah. Menurut pendapat dan pengalaman saya, jawaban tidak harus persis dengan apa yang diminta OP, asalkan terkait dengan itu dan memberikan informasi tambahan yang berguna bagi pembaca. Dengan tidak memilih jawaban seperti itu, Anda tidak akan memberikan jawaban yang bermanfaat.
Zvi
1
Meskipun ini umumnya benar - terutama untuk pertanyaan luas - pertanyaan ini sangat jelas dalam karakternya. Ada banyak pertanyaan yang meminta solusi eksplisit melalui storyboard, dan jika memungkinkan pembaca ingin memiliki solusi yang menggunakan storyboard, kemudian menambahkan "storyboard" ke istilah pencarian google cukup untuk menunjukkan pertanyaan-pertanyaan ini. Saya tidak mengatakan jawaban Anda buruk, tetapi di tempat yang salah.
mengembalikan true
Karena saya menemukan posting ini sebagai yang paling dekat untuk mengatasi masalah saya, saya mempostingnya di sini. Mengikuti jalan Anda berarti bahwa saya seharusnya menulis posting baru dan menjawab posting saya, yang bagi saya tampaknya sedikit berlebihan.
Zvi
1
Saya telah menemukan pertanyaan-pertanyaan yang tampaknya mengatasi masalah Anda lebih baik daripada yang ini: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
mengembalikan true
6

Anda dapat menggunakan subkelas UIButton ini untuk menyesuaikan UIButton sesuai kebutuhan Anda.

kunjungi repo github ini untuk referensi

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
sumber
siapa pun downvoted ini, u bahkan mencoba untuk bekerja dengan cara ini ??? hanya tidak buang waktu downvoting Anda
Nikesh Jha
Mengapa downvote? ini tampaknya berfungsi dan sangat bersih.
ColdGrub1384
@ ColdGrub1384 persis :)
Nikesh Jha
3

Saya pikir cara termudah dan terbersih, adalah menggunakan protokol untuk menghindari pewarisan dan pengulangan kode. Anda dapat mengubah properti ini langsung dari 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 }
    }
}

Memperbarui

Di tautan ini Anda dapat menemukan contoh dengan utilitas protokol yang dapat dilacak

masukkan deskripsi gambar di sini

Hamza
sumber
2
Anda tidak benar-benar membutuhkan protokol di sini. Bekerja dengan baik tanpa itu.
mengembalikan true
2
Hai @returntrue, saya tidak tahu mengapa Anda downvote jawaban saya ... Saya pikir Anda tidak memahaminya, saya mengundang Anda untuk menonton sesi ini: developer.apple.com/videos/play/wwdc2015/408 Mengapa menggunakan protokol? protokol memungkinkan lebih banyak fleksibilitas, saya dapat memiliki implementasi standar, dll. Jawaban saya memungkinkan untuk mengedit properti ini di storyboard, dan saya tidak perlu mengulangi kode atau subkelas kelas apa pun Jawaban Anda benar tetapi di sini saya berbagi cara lain untuk melakukan Jika Anda membatalkan semua jawaban lain, Anda harus ingat bahwa Stackoverflow adalah untuk berbagi pengetahuan kami sebelum mendapatkan reputasi
Hamza
2
Benar, kode Anda memungkinkan semua hal yang Anda daftarkan. TETAPI, karena UIView adalah superclass dari semua elemen UI, ekstensi UIView Anda cukup untuk melakukan pekerjaan itu. Protokol Traceable tidak diperlukan dengan cara apa pun dan tidak menghasilkan perbaikan apa pun (karena hanya diterjemahkan ke UIView - hanya UIViews dan subkelasnya yang dapat dilacak).
mengembalikan true
1
hai @ returntrue, Anda benar dalam kasus penggunaan ini, mungkin, kami tidak memerlukan protokol, tetapi dengan itu kami memiliki lebih banyak fleksibilitas, dan misalnya, kami dapat menambahkan nilai default untuk diterapkan dan hanya tersedia untuk UIImageView dan UIButton, saya akan memperbarui jawaban saya dengan contoh ini :)
Hamza
1
Itu tidak benar-benar masuk akal untuk menerapkan nilai-nilai default untuk properti seperti warna batas sudut radius atau sudut. Nilai-nilai itu harus diterapkan ke setiap instance tampilan di UI Anda secara terpisah dengan cara yang bermakna.
mengembalikan 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
sumber
1

sebagai samping tip pastikan tombol Anda bukan subkelas dari kelas khusus di papan cerita, dalam hal ini kode Anda tempat terbaik harus di kelas kustom itu sendiri menyebabkan kode hanya bekerja di luar kelas kustom jika tombol Anda subkelas dari default Kelas UIButton dan outletnya, semoga ini bisa membantu orang bertanya-tanya mengapa radio sudut tidak berlaku pada tombol saya dari kode.

Isha
sumber
0
@IBOutlet weak var button: UIButton!

...

Saya pikir untuk radius cukup dengan parameter ini:

button.layer.cornerRadius = 5
Iryna Batvina
sumber
Ini sepertinya tidak menambahkan sesuatu yang baru.
mengembalikan true
0

COBALAH Tombol Border Ini Dengan Sudut Bulat

anyButton.backgroundColor = .clear

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

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor
Raghib Arshi
sumber
ya, tapi memperkenalkan ide menggunakan cornerRadius yang dinamis, saya tidak berpikir itu hanya disalin dan ditempelkan.
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
sumber
-1

Anda dapat subkelas UIButtondan menambahkan @IBInspectablevariabel ke dalamnya sehingga Anda dapat mengonfigurasi parameter tombol khusus melalui StoryBoard "Attribute Inspector". Di bawah ini saya menuliskan kode itu.

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

Saya pikir ini adalah bentuk sederhana

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