Làm cách nào để tạo một nút có đường viền tròn trong Swift?


232

Tôi đang xây dựng một ứng dụng bằng cách sử dụng swift trong phiên bản Xcode 6 mới nhất và muốn biết làm thế nào tôi có thể sửa đổi nút của mình để nó có thể có đường viền tròn mà tôi có thể tự điều chỉnh nếu cần. Khi đã xong, làm thế nào tôi có thể thay đổi màu của đường viền mà không cần thêm nền cho nó? Nói cách khác, tôi muốn một nút hơi tròn không có nền, chỉ có viền 1pt của một màu nhất định.

Câu trả lời:


528

Sử dụng button.layer.cornerRadius, button.layer.borderColorbutton.layer.borderWidth. Lưu ý rằng borderColoryêu cầu a CGColor, vì vậy bạn có thể nói (Swift 3/4):

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

2
Tôi đã thử điều này, nhưng tôi gặp một vấn đề nhỏ là văn bản đầu tiên bắt đầu từ chính đường viền, vì vậy nó không có gì hay
ho

2
@ vinbhai4u Hãy thử sử dụng titleEdgeInsets.
trả lại đúng

Làm thế nào chúng ta có thể làm cho hộp lớn hơn văn bản nút?
Sớm hơn

tạo một lối thoát cho nút để tham khảo nó
Việc làm

tôi muốn sử dụng nó như thế này nhưng không hoạt động :( UIButton.appparent (). layer.cornerRadius = 4
MR

65

Để thực hiện công việc này trong bảng phân cảnh (Trình tạo giao diện)

Với sự giúp đỡ của IBDesignable, chúng tôi có thể thêm nhiều tùy chọn hơn vào Trình tạo giao diện Trình tạo UIButtonvà chỉnh sửa chúng trên bảng phân cảnh. Đầu tiên, thêm mã sau vào dự án của bạn.

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

Sau đó, chỉ cần đặt các thuộc tính cho các nút trên bảng phân cảnh.

nhập mô tả hình ảnh ở đây


4
Đây ít nhiều là bản sao câu trả lời dưới đây của Hamza Ghazouani.
trả lại đúng

Khi tôi sử dụng giải pháp trên cho boarderColor, nó bị lỗi trong interacebuilder, tôi sử dụng đoạn mã dưới đây nhưng tôi thành công nếu tôi sử dụng didset thay vì set và get. @IBInspectable var BorderColor: UIColor = .red {// did Set {// layer.borderColor = BorderColor.cgColor //} set {Guard let uiColor = newValue other {return} layer.borderColor = uiColor.cgColor} = layer.borderColor other {return nil} return UIColor (cgColor: color)}}
Amr Angry

1
Wow, giống như phép thuật! Cảm ơn bạn! Nhân viên của Google - đảm bảo đặt đoạn mã này hoàn toàn bên ngoài dấu ngoặc nhọn cuối cùng của bạn trong tệp bạn đang đặt - đoạn mã không nên nằm trong bất kỳ lớp hoặc hàm nào
velkoon

24

Tôi đã tạo một lớp con UIButton đơn giản sử dụng tintColorcho màu văn bản và đường viền của nó và khi được tô sáng sẽ thay đổi nền của nó thành 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)
}
}

Điều này sử dụng tiện ích mở rộng UIImage tạo hình ảnh từ một màu, tôi tìm thấy mã đó ở đây: https://stackoverflow.com/a/33675160

Nó hoạt động tốt nhất khi được đặt để nhập Tùy chỉnh trong trình tạo giao diện làm Kiểu hệ thống mặc định thay đổi một chút màu sắc khi nút được tô sáng.


13

Lớp này dựa trên tất cả các ý kiến ​​và đề xuất trong câu trả lời, và cũng có thể được thiết kế trực tiếp từ xcode. Sao chép vào dự án của bạn và chèn bất kỳ UIButton nào và thay đổi để sử dụng lớp tùy chỉnh, bây giờ chỉ cần chọn đường viền hoặc màu nền từ xcode cho trạng thái bình thường và / hoặc được tô sáng.

//
//  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
Tại sao nhập khẩu Foundationkhi nhập UIKitthực sự đủ?
trả lại đúng

@returntrue Có bạn đúng, Nền tảng không bắt buộc, tôi đến từ mẫu cơ bản xcode gốc nếu tôi không sai. Tôi đã cập nhật mã.
le0diaz

Chỉ có điều là điều này sẽ làm cho nút tròn mọi lúc. Có lẽ bạn có thể thêm một thuộc tính bán kính góc để sửa lỗi này quá.
Andreas777

10

Dựa trên câu trả lời @returntrue, tôi đã quản lý để triển khai nó trong Trình tạo giao diện.

Để có được nút góc tròn bằng Trình tạo giao diện, hãy thêm Khóa Path = "layer.cornerRadius"Type = "Number"Value = "10"(hoặc giá trị khác theo yêu cầu) trong " User Defined RunTime Attribute" của Identity Inspectornút.


1
Điều này thực sự hoạt động, nhưng không phải là cụ thể của Swift. Câu hỏi hỏi rõ ràng làm thế nào để đạt được hiệu quả mong muốn bằng cách sử dụng Swift, không phải bằng cách sử dụng bảng phân cảnh hoặc tương tự.
trả lại đúng

2
Câu trả lời của tôi không hướng đến OP mà là những độc giả khác phải đối mặt với vấn đề của tôi. và chỉ bằng cách tìm bài này đã có thể giải quyết vấn đề. Theo quan điểm và kinh nghiệm của tôi, câu trả lời không chính xác với những gì OP yêu cầu, miễn là chúng có liên quan đến nó và cung cấp thêm thông tin hữu ích cho độc giả. Bằng cách bỏ phiếu trả lời như vậy, bạn không khuyến khích câu trả lời hữu ích.
Zvi

1
Trong khi điều này nói chung là đúng - đặc biệt đối với các câu hỏi rộng - chính câu hỏi này rất rõ ràng trong tính cách của nó. Có vô số câu hỏi yêu cầu một giải pháp rõ ràng thông qua bảng phân cảnh và nếu độc giả có thể muốn có một giải pháp sử dụng bảng phân cảnh, sau đó thêm "bảng phân cảnh" vào cụm từ tìm kiếm của Google đủ để hiển thị những câu hỏi này. Tôi không nói câu trả lời của bạn là xấu, mà là ở sai chỗ.
trở về đúng vào

Vì tôi thấy bài đăng này là gần nhất để giải quyết vấn đề của tôi, tôi đã đăng nó ở đây. Đi theo cách của bạn có nghĩa là tôi nên viết một bài mới và trả lời bài viết của tôi, dường như, với tôi, một chút quá mức cần thiết.
Zvi

1
Tôi đã tìm thấy những câu hỏi dường như giải quyết vấn đề của bạn tốt hơn câu hỏi này: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
trả lại đúng

6

Bạn có thể sử dụng lớp con này của UIButton để tùy chỉnh UIButton theo nhu cầu của bạn.

truy cập repo github này để tham khảo

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

Bất cứ ai đã đánh giá thấp điều này, bạn thậm chí đã cố gắng làm việc theo cách này chưa? Chỉ cần không lãng phí thời gian của bạn xuống
Nikesh Jha

Tại sao một downvote? cái này có vẻ hiệu quả và rất sạch sẽ
ColdGrub1384

@ ColdGrub1384 chính xác :)
Nikesh Jha

3

Tôi nghĩ cách dễ nhất và sạch nhất, là sử dụng giao thức để tránh sự lặp lại và kế thừa mã. Bạn có thể thay đổi thuộc tính này trực tiếp từ bảng phân cảnh

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

Cập nhật

Trong liên kết này, bạn có thể tìm thấy một ví dụ với tiện ích của giao thức Tracizable

nhập mô tả hình ảnh ở đây


2
Bạn không thực sự cần giao thức ở đây. Hoạt động hoàn toàn tốt mà không có nó.
trả lại đúng

2
Xin chào @returntrue, tôi không biết lý do tại sao bạn lại hạ thấp câu trả lời của tôi ... Tôi nghĩ bạn không hiểu nó, tôi mời bạn xem phiên này: developer.apple.com/ideo/play/wwdc2015/408 Tại sao nên sử dụng giao thức? giao thức cho phép linh hoạt hơn, tôi có thể thực hiện mặc định, v.v ... Câu trả lời của tôi cho phép chỉnh sửa các thuộc tính này trong bảng phân cảnh và tôi không cần lặp lại mã hoặc phân lớp bất kỳ lớp nào Câu trả lời của bạn là đúng nhưng ở đây tôi đã chia sẻ một cách khác để làm Bạn đánh giá thấp tất cả các câu trả lời khác, bạn nên nhớ rằng Stackoverflow là để chia sẻ kiến ​​thức của chúng tôi trước khi kiếm được danh tiếng
Hamza

2
Chính xác, mã của bạn cho phép tất cả những điều bạn liệt kê. NHƯNG, vì UIView là siêu lớp của tất cả các thành phần UI, tiện ích mở rộng UIView của bạn đủ để thực hiện công việc. Giao thức Tracizable không được yêu cầu theo bất kỳ cách nào và không mang lại bất kỳ cải tiến nào (vì nó chỉ đơn giản là dịch sang UIView - chỉ UIView và các lớp con của nó có thể theo dõi được).
trả lại đúng

1
xin chào sẽ cập nhật câu trả lời của tôi với ví dụ này :)
Hamza

1
Không thực sự có ý nghĩa khi áp dụng các giá trị mặc định cho các thuộc tính như màu đường viền bán kính góc. Những giá trị này nên được áp dụng cho từng trường hợp xem trong giao diện người dùng của bạn riêng biệt trong một cách có ý nghĩa.
trả lại đúng

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

vì mẹo bên cạnh đảm bảo nút của bạn không phải là lớp con của bất kỳ lớp tùy chỉnh nào trong bảng câu chuyện, trong trường hợp như vậy, mã tốt nhất của bạn phải ở trong lớp tùy chỉnh, nó tự tạo ra mã chỉ hoạt động ngoài lớp tùy chỉnh nếu nút của bạn là lớp con của mặc định Lớp UIButton và đầu ra của nó, hy vọng điều này có thể giúp bất cứ ai tự hỏi tại sao radio góc không áp dụng trên nút của tôi từ mã.


0
@IBOutlet weak var button: UIButton!

...

Tôi nghĩ cho bán kính vừa đủ tham số này:

button.layer.cornerRadius = 5

Điều này không thêm bất cứ điều gì mới, có vẻ như.
trả lại đúng

0

TRY NÀY Nút viền với góc tròn

anyButton.backgroundColor = .clear

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

anyButton.layer.borderWidth = 1

anyButton.layer.borderColor = UIColor.black.cgColor

vâng, nhưng nó giới thiệu ý tưởng sử dụng angleRadius rất năng động, tôi không nghĩ rằng nó chỉ đơn giản là sao chép và dán.
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

Bạn có thể phân lớp UIButtonvà thêm @IBInspectablecác biến vào đó để bạn có thể định cấu hình các tham số nút tùy chỉnh thông qua "Trình kiểm tra thuộc tính" của StoryBoard. Dưới đây tôi viết mã đó.

@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

Tôi nghĩ rằng đây là hình thức đơn giản

        Button1.layer.cornerRadius = 10(Half of the length and width)
        Button1.layer.borderWidth = 2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.