ฉันจะทำให้ปุ่มมีขอบโค้งมนใน Swift ได้อย่างไร


232

ฉันกำลังสร้างแอพโดยใช้ swift ในเวอร์ชันล่าสุดของ Xcode 6 และต้องการทราบว่าฉันจะแก้ไขปุ่มอย่างไรเพื่อให้สามารถมีเส้นขอบกลมที่ฉันสามารถปรับตัวเองได้ถ้าจำเป็น เมื่อเสร็จแล้วฉันจะเปลี่ยนสีของเส้นขอบเองได้อย่างไรโดยไม่ต้องเพิ่มพื้นหลังลงไป กล่าวอีกนัยหนึ่งฉันต้องการปุ่มกลมที่ไม่มีพื้นหลังเพียง 1 พอยต์ของสีที่ต้องการ

คำตอบ:


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เราสามารถเพิ่มตัวเลือกเพิ่มเติมเพื่อตัวสร้างส่วนต่อประสานตัวสร้าง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
นี่คือสำเนาของคำตอบด้านล่างของ Hamza Ghazouani มากหรือน้อย
ส่งคืนจริงใน

เมื่อฉันใช้วิธีการแก้ปัญหาข้างต้นสำหรับ boarderColor มันสร้างล้มเหลวใน interfacebuilder ฉันใช้รหัสด้านล่าง แต่ฉันประสบความสำเร็จถ้าฉันใช้ didset แทนที่จะตั้งและรับ @IBInspectable var borderColor: UIColor = .red {// didSet {// layer.borderColor = borderColor.cgColor //} ตั้งค่า {Guard ให้ uiColor = newValue อื่น {return} layer.borderColor = uiColor.cgColor} = layer.borderColor else {return nil} return UIColor (cgColor: color)}}
Amr Angry

1
ว้าวเหมือนเวทมนตร์! ขอบคุณ! ชาว Google - ตรวจสอบให้แน่ใจว่าได้วางรหัสนี้อย่างสมบูรณ์นอกวงเล็บปีกกาสุดท้ายของคุณในไฟล์ที่คุณวางไว้ - ข้อมูลโค้ดไม่ควรอยู่ในชั้นเรียนหรือฟังก์ชั่น
velkoon

24

ฉันได้สร้างง่ายๆ sublcass UIButton ที่ใช้สำหรับข้อความและเส้นขอบสีไฮไลต์และเมื่อมีการเปลี่ยนแปลงพื้นหลังของมันไปtintColortintColor

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 answer ฉันจัดการเพื่อนำไปใช้ใน Interface Builder

ที่จะได้รับปุ่มมุมรอบใช้ Interface Builder เพิ่มคีย์Path = "layer.cornerRadius"ด้วยType = "Number"และValue = "10"(หรือค่าอื่น ๆ ที่จำเป็น) ใน " User Defined RunTime Attribute" ของIdentity Inspectorปุ่ม


1
นี่ใช้งานได้จริง แต่ไม่ใช่เฉพาะสวิฟท์ คำถามถามอย่างชัดเจนว่าจะบรรลุผลตามที่ต้องการได้อย่างไรโดยใช้ Swift ไม่ใช่โดยใช้สตอรีบอร์ดหรือสิ่งที่คล้ายกัน
ส่งคืนจริงเมื่อ

2
คำตอบของฉันไม่ได้ถูกนำไปที่ OP แต่ไปยังผู้อ่านคนอื่น ๆ ที่ประสบปัญหาของฉัน และโดยการค้นหาโพสต์นี้เท่านั้นที่สามารถแก้ปัญหาได้ ในความเห็นและประสบการณ์ของฉันคำตอบไม่ตรงกับสิ่งที่ OP ถามตราบใดที่พวกเขาเกี่ยวข้องและให้ข้อมูลเพิ่มเติมที่เป็นประโยชน์ต่อผู้อ่าน เมื่อลงคะแนนเสียงให้กับคำตอบนั้นคุณจะไม่สนับสนุนคำตอบที่มีประโยชน์
ซวี่

1
แม้ว่าโดยทั่วไปแล้วจะเป็นเรื่องจริง - โดยเฉพาะสำหรับคำถามทั่วไป - คำถามนี้ชัดเจนในลักษณะของมัน มีคำถามมากมายที่ถามหาวิธีแก้ปัญหาอย่างชัดเจนผ่านสตอรีบอร์ดและหากผู้อ่านที่เป็นไปได้ต้องการมีวิธีแก้ปัญหาที่ใช้สตอรีบอร์ดแล้วเพิ่ม "สตอรีบอร์ด" ในคำค้นหาของ Google เพื่อแสดงคำถามเหล่านี้ ฉันไม่ได้กำลังบอกว่าคำตอบของคุณไม่ดี แต่อยู่ในสถานที่ที่ไม่ถูกต้อง
ส่งคืนจริงใน

เนื่องจากฉันพบว่าโพสต์นี้ใกล้เคียงกับปัญหาของฉันมากที่สุดฉันจึงโพสต์ไว้ที่นี่ ไปตามทางของคุณหมายความว่าฉันควรจะเขียนโพสต์ใหม่และตอบโพสต์ของฉันซึ่งดูเหมือนว่าฉัน overkill เล็กน้อย
ซวี่

1
ฉันได้พบคำถามเหล่านั้นที่ดูเหมือนจะแก้ไขปัญหาของคุณดีขึ้นกว่านี้: stackoverflow.com/questions/12301256 ; stackoverflow.com/questions/20477990
ส่งคืนจริง

6

คุณสามารถใช้คลาสย่อยของ UIButton เพื่อปรับแต่ง UIButton ตามความต้องการของคุณ

เยี่ยมชม repo 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()
    }
}

ใครก็ตามที่ลงคะแนนให้คุณลองใช้วิธีนี้หรือไม่อย่าเสียเวลาในการลงคะแนน
Nikesh 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 }
    }
}

ปรับปรุง

ในลิงค์นี้คุณสามารถค้นหาตัวอย่างด้วยยูทิลิตี้ของโปรโตคอลที่ตรวจสอบย้อนกลับได้

ป้อนคำอธิบายรูปภาพที่นี่


2
คุณไม่ต้องการโปรโตคอลจริงๆ ทำงานได้ดีอย่างสมบูรณ์โดยไม่ต้องมัน
ส่งคืน

2
สวัสดี @ ผลตอบแทนที่แท้จริงฉันไม่รู้ว่าทำไมคุณถึงลงคำตอบของฉัน ... ฉันคิดว่าคุณไม่เข้าใจฉันขอเชิญคุณดูเซสชั่นนี้: developer.apple.com/th/play/wwdc2015/408ทำไมต้องใช้โปรโตคอล? โปรโตคอลช่วยให้มีความยืดหยุ่นมากขึ้นฉันสามารถเริ่มต้นใช้งานได้ ฯลฯ คำตอบของฉันอนุญาตให้แก้ไขคุณสมบัตินี้ในกระดานเรื่องราวและฉันไม่จำเป็นต้องทำซ้ำรหัสหรือคลาสย่อยใด ๆ คลาสใดคำตอบของคุณถูกต้อง แต่ที่นี่ฉันใช้วิธีอื่นร่วมกัน คุณต้องตอบคำถามอื่น ๆ ทั้งหมดคุณควรระลึกไว้เสมอว่า Stackoverflow สำหรับการแบ่งปันความรู้ของเราก่อนที่จะได้รับชื่อเสียง
Hamza

2
ถูกต้องรหัสของคุณอนุญาตให้ทุกสิ่งที่คุณระบุไว้ในรายการ แต่เนื่องจาก UIView เป็นซุปเปอร์คลาสขององค์ประกอบ UI ทั้งหมดส่วนขยาย UIView ของคุณก็เพียงพอต่อการทำงาน โปรโตคอลที่ตรวจสอบย้อนกลับนั้นไม่จำเป็นต้องใช้ในทางใดทางหนึ่งและไม่ได้ให้การปรับปรุงใด ๆ (เพราะมันแปลเป็น UIView เพียง UIViews เท่านั้นและ subclasses ของมันจะติดตามได้)
ส่งคืนจริงเมื่อ

1
สวัสดี @ ผลตอบแทนที่แท้จริงคุณอยู่ในกรณีการใช้งานนี้อาจเป็นเพราะเราไม่ต้องการโปรโตคอล แต่เนื่องจากเรามีความยืดหยุ่นมากขึ้นและตัวอย่างเช่นเราสามารถเพิ่มค่าเริ่มต้นที่จะใช้และใช้ได้เฉพาะกับ UIImageView และ UIButton ฉันเท่านั้น จะปรับปรุงคำตอบของฉันกับตัวอย่างนี้ :)
Hamza

1
ไม่เหมาะสมที่จะใช้ค่าเริ่มต้นสำหรับคุณสมบัติเช่นสีเส้นขอบรัศมีหรือมุมขอบ ควรใช้ค่าเหล่านั้นกับอินสแตนซ์มุมมองแต่ละรายการใน UI ของคุณแยกกันอย่างมีความหมาย
ส่งคืนจริงเมื่อ

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 class และทางออกของมันหวังว่านี่จะช่วยให้ทุกคนสงสัยว่าทำไมวิทยุแบบเข้ามุมไม่สามารถใช้กับปุ่มของฉันได้จากรหัส


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.