按钮的圆角半径和阴影问题

问题描述 投票:0回答:15

我正在尝试创建一个带有 圆角 投影 的按钮。无论我如何切换,该按钮都无法正确显示。我尝试过

masksToBounds = false
masksToBounds = true
,但要么是圆角半径起作用而阴影不起作用,要么是阴影起作用而圆角半径不会剪切按钮的角。

import UIKit
import QuartzCore

@IBDesignable
class Button : UIButton
{
    @IBInspectable var masksToBounds: Bool    = false                {didSet{updateLayerProperties()}}
    @IBInspectable var cornerRadius : CGFloat = 0                    {didSet{updateLayerProperties()}}
    @IBInspectable var borderWidth  : CGFloat = 0                    {didSet{updateLayerProperties()}}
    @IBInspectable var borderColor  : UIColor = UIColor.clearColor() {didSet{updateLayerProperties()}}
    @IBInspectable var shadowColor  : UIColor = UIColor.clearColor() {didSet{updateLayerProperties()}}
    @IBInspectable var shadowOpacity: CGFloat = 0                    {didSet{updateLayerProperties()}}
    @IBInspectable var shadowRadius : CGFloat = 0                    {didSet{updateLayerProperties()}}
    @IBInspectable var shadowOffset : CGSize  = CGSizeMake(0, 0)     {didSet{updateLayerProperties()}}

    override func drawRect(rect: CGRect)
    {
        updateLayerProperties()
    }

    func updateLayerProperties()
    {
        self.layer.masksToBounds = masksToBounds
        self.layer.cornerRadius = cornerRadius
        self.layer.borderWidth = borderWidth
        self.layer.borderColor = borderColor.CGColor
        self.layer.shadowColor = shadowColor.CGColor
        self.layer.shadowOpacity = CFloat(shadowOpacity)
        self.layer.shadowRadius = shadowRadius
        self.layer.shadowOffset = shadowOffset
    }
}
ios swift uibutton shadow cornerradius
15个回答
172
投票

以下 Swift 5 / iOS 12 代码显示了如何设置

UIButton
的子类,该子类允许创建带有圆角和周围阴影的实例:

import UIKit

final class CustomButton: UIButton {

    private var shadowLayer: CAShapeLayer!

    override func layoutSubviews() {
        super.layoutSubviews()

        if shadowLayer == nil {
            shadowLayer = CAShapeLayer()
            shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath
            shadowLayer.fillColor = UIColor.white.cgColor

            shadowLayer.shadowColor = UIColor.darkGray.cgColor
            shadowLayer.shadowPath = shadowLayer.path
            shadowLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
            shadowLayer.shadowOpacity = 0.8
            shadowLayer.shadowRadius = 2

            layer.insertSublayer(shadowLayer, at: 0)
            //layer.insertSublayer(shadowLayer, below: nil) // also works
        }        
    }

}

根据您的需要,您可以在 Storyboard 中添加

UIButton
并将其类设置为
CustomButton
,或者您可以通过编程方式创建
CustomButton
的实例。以下
UIViewController
实现展示了如何以编程方式创建和使用
CustomButton
实例:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = CustomButton(type: .system)
        button.setTitle("Button", for: .normal)
        view.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor)        
        let widthConstraint = button.widthAnchor.constraint(equalToConstant: 100)
        let heightConstraint = button.heightAnchor.constraint(equalToConstant: 100)
        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
    }

}

前面的代码在 iPhone 模拟器中生成下面的图像:


48
投票

我的自定义按钮带有一些阴影圆角,我直接在

Storyboard
中使用它,无需以编程方式触摸它。

斯威夫特4

class RoundedButtonWithShadow: UIButton {
    override func awakeFromNib() {
        super.awakeFromNib()
        self.layer.masksToBounds = false
        self.layer.cornerRadius = self.frame.height/2
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
        self.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
        self.layer.shadowOpacity = 0.5
        self.layer.shadowRadius = 1.0
    }
}


18
投票

为了扩展 Imanou 的帖子,可以以编程方式在自定义按钮类中添加阴影层

@IBDesignable class CustomButton: UIButton {
    var shadowAdded: Bool = false

    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
            layer.masksToBounds = cornerRadius > 0
        }
    }

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        if shadowAdded { return }
        shadowAdded = true

        let shadowLayer = UIView(frame: self.frame)
        shadowLayer.backgroundColor = UIColor.clearColor()
        shadowLayer.layer.shadowColor = UIColor.darkGrayColor().CGColor
        shadowLayer.layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: self.cornerRadius).CGPath
        shadowLayer.layer.shadowOffset = CGSize(width: 1.0, height: 1.0)
        shadowLayer.layer.shadowOpacity = 0.5
        shadowLayer.layer.shadowRadius = 1
        shadowLayer.layer.masksToBounds = true
        shadowLayer.clipsToBounds = false

        self.superview?.addSubview(shadowLayer)
        self.superview?.bringSubviewToFront(self)
    }
}

15
投票

获得更可用且一致的按钮的另一种方法。

斯威夫特2:

func getImageWithColor(color: UIColor, size: CGSize, cornerRadius:CGFloat) -> UIImage {
    let rect = CGRectMake(0, 0, size.width, size.height)
    UIGraphicsBeginImageContextWithOptions(size, false, 1)
    UIBezierPath(
        roundedRect: rect,
        cornerRadius: cornerRadius
        ).addClip()
    color.setFill()
    UIRectFill(rect)
    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

let button = UIButton(type: .Custom)
button.frame = CGRectMake(20, 20, 200, 50)
button.setTitle("My Button", forState: UIControlState.Normal)
button.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
self.addSubview(button)

let image = getImageWithColor(UIColor.whiteColor(), size: button.frame.size, cornerRadius: 5)
button.setBackgroundImage(image, forState: UIControlState.Normal)

button.layer.shadowRadius = 5
button.layer.shadowColor = UIColor.blackColor().CGColor
button.layer.shadowOpacity = 0.5
button.layer.shadowOffset = CGSizeMake(0, 1)
button.layer.masksToBounds = false

斯威夫特3:

func getImageWithColor(_ color: UIColor, size: CGSize, cornerRadius:CGFloat) -> UIImage? {
    let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    color.setFill()
    UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip()
    color.setFill()
    UIRectFill(rect)
    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return image
}

let button = UIButton(type: .custom)
button.frame = CGRect(x:20, y:20, width:200, height:50)
button.setTitle("My Button", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
self.addSubview(button)

if let image = getImageWithColor(UIColor.white, size: button.frame.size, cornerRadius: 5) {
    button.setBackgroundImage(image, for: .normal)
}

button.layer.shadowRadius = 5
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOpacity = 0.5
button.layer.shadowOffset = CGSize(width:0, height:1)
button.layer.masksToBounds = false

13
投票

斯威夫特 5 & 不需要“UIBezierPath”

    view.layer.cornerRadius = 15
    view.clipsToBounds = true
    view.layer.masksToBounds = false
    view.layer.shadowRadius = 7
    view.layer.shadowOpacity = 0.6
    view.layer.shadowOffset = CGSize(width: 0, height: 5)
    view.layer.shadowColor = UIColor.red.cgColor

4
投票

重构了this以支持任何视图。从此视图子类化,它应该有圆角。如果您将 UIVisualEffectView 之类的内容添加为该视图的子视图,您可能需要在该 UIVisualEffectView 上使用相同的圆角,否则它将不会有圆角。

/// Inspiration: https://stackoverflow.com/a/25475536/129202
class ViewWithRoundedcornersAndShadow: UIView {
    private var theShadowLayer: CAShapeLayer?

    override func layoutSubviews() {
        super.layoutSubviews()

        if self.theShadowLayer == nil {
            let rounding = CGFloat.init(22.0)

            let shadowLayer = CAShapeLayer.init()
            self.theShadowLayer = shadowLayer
            shadowLayer.path = UIBezierPath.init(roundedRect: bounds, cornerRadius: rounding).cgPath
            shadowLayer.fillColor = UIColor.clear.cgColor

            shadowLayer.shadowPath = shadowLayer.path
            shadowLayer.shadowColor = UIColor.black.cgColor
            shadowLayer.shadowRadius = CGFloat.init(3.0)
            shadowLayer.shadowOpacity = Float.init(0.2)
            shadowLayer.shadowOffset = CGSize.init(width: 0.0, height: 4.0)

            self.layer.insertSublayer(shadowLayer, at: 0)
        }
    }
}

3
投票

2020语法精确解

import UIKit
class ColorAndShadowButton: UIButton {
    override init(frame: CGRect) { super.init(frame: frame), common() }
    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder), common() }
    private func common() {
        // UIButton is tricky: you MUST set the clear bg in bringup;  NOT in layout
        backgroundColor = .clear
        clipsToBounds = false
        layer.insertSublayer(colorAndShadow, below: layer)
    }
    
   lazy var colorAndShadow: CAShapeLayer = {
        let s = CAShapeLayer()
        // set your button color HERE (NOT on storyboard)
        s.fillColor = UIColor.black.cgColor
        // now set your shadow color/values
        s.shadowColor = UIColor.red.cgColor
        s.shadowOffset = CGSize(width: 0, height: 10)
        s.shadowOpacity = 1
        s.shadowRadius = 10
        // now add the shadow
        layer.insertSublayer(s, at: 0)
        return s
    }()
    
    override func layoutSubviews() {
        super.layoutSubviews()
        // you MUST layout these two EVERY layout cycle:
        colorAndShadow.frame = bounds
        colorAndShadow.path = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath
    }
}

  • 请注意,这里非常旧的最佳答案是正确的,但有一个严重错误

请注意,不幸的是,
UIButton
与 iOS 中的
UIView
有很大不同。

  • 由于 iOS 中的奇怪行为,您必须在初始化中设置背景颜色(在这种情况下当然必须清晰),而不是在布局中。您可以在故事板中将其设置为清晰(但您通常将其单击为某种纯色,以便您在故事板中工作时可以看到它。)
总的来说,阴影/舍入的组合在 iOS 中是一个真正的痛苦。类似解决方案:

https://stackoverflow.com/a/57465440/294884 - 图像+圆角+阴影
https://stackoverflow.com/a/41553784/294884 - 两角问题
https://stackoverflow.com/a/59092828/294884 - “阴影+洞”或“发光盒”问题
https://stackoverflow.com/a/57400842/294884 - “边界和间隙”问题
https://stackoverflow.com/a/57514286/294884 - 基本“添加”贝塞尔曲线


3
投票

带阴影的圆角半径

简短而简单的方法!!!!!!

extension CALayer { func applyCornerRadiusShadow( color: UIColor = .black, alpha: Float = 0.5, x: CGFloat = 0, y: CGFloat = 2, blur: CGFloat = 4, spread: CGFloat = 0, cornerRadiusValue: CGFloat = 0) { cornerRadius = cornerRadiusValue shadowColor = color.cgColor shadowOpacity = alpha shadowOffset = CGSize(width: x, height: y) shadowRadius = blur / 2.0 if spread == 0 { shadowPath = nil } else { let dx = -spread let rect = bounds.insetBy(dx: dx, dy: dx) shadowPath = UIBezierPath(rect: rect).cgPath } } }
代码的使用

btn.layer.applyCornerRadiusShadow(color: .black, alpha: 0.38, x: 0, y: 3, blur: 10, spread: 0, cornerRadiusValue: 24)

无需 maskToBound

请验证 ClipsToBounds 是否为 false。

输出


2
投票
为了改进 PiterPan 的答案并在 Swift 3 中使用圆形按钮显示真实的阴影(不仅仅是没有模糊的背景):

override func viewDidLoad() { super.viewDidLoad() myButton.layer.masksToBounds = false myButton.layer.cornerRadius = myButton.frame.height/2 myButton.clipsToBounds = true } override func viewDidLayoutSubviews() { addShadowForRoundedButton(view: self.view, button: myButton, opacity: 0.5) } func addShadowForRoundedButton(view: UIView, button: UIButton, opacity: Float = 1) { let shadowView = UIView() shadowView.backgroundColor = UIColor.black shadowView.layer.opacity = opacity shadowView.layer.shadowRadius = 5 shadowView.layer.shadowOpacity = 0.35 shadowView.layer.shadowOffset = CGSize(width: 0, height: 0) shadowView.layer.cornerRadius = button.bounds.size.width / 2 shadowView.frame = CGRect(origin: CGPoint(x: button.frame.origin.x, y: button.frame.origin.y), size: CGSize(width: button.bounds.width, height: button.bounds.height)) self.view.addSubview(shadowView) view.bringSubview(toFront: button) }
    

0
投票
扩展阴影和圆角半径

extension UIView { func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, shadowRadius: CGFloat = 1, scale: Bool = true, cornerRadius: CGFloat) { let shadowLayer = CAShapeLayer() shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath shadowLayer.fillColor = UIColor.white.cgColor shadowLayer.shadowColor = color.cgColor shadowLayer.shadowPath = shadowLayer.path shadowLayer.shadowOffset = offSet shadowLayer.shadowOpacity = opacity shadowLayer.shadowRadius = shadowRadius layer.insertSublayer(shadowLayer, at: 0) } }
    

0
投票
这是可行的解决方案!

extension UIView { func applyShadowWithCornerRadius(color:UIColor, opacity:Float, radius: CGFloat, edge:AIEdge, shadowSpace:CGFloat) { var sizeOffset:CGSize = CGSize.zero switch edge { case .Top: sizeOffset = CGSize(width: 0, height: -shadowSpace) case .Left: sizeOffset = CGSize(width: -shadowSpace, height: 0) case .Bottom: sizeOffset = CGSize(width: 0, height: shadowSpace) case .Right: sizeOffset = CGSize(width: shadowSpace, height: 0) case .Top_Left: sizeOffset = CGSize(width: -shadowSpace, height: -shadowSpace) case .Top_Right: sizeOffset = CGSize(width: shadowSpace, height: -shadowSpace) case .Bottom_Left: sizeOffset = CGSize(width: -shadowSpace, height: shadowSpace) case .Bottom_Right: sizeOffset = CGSize(width: shadowSpace, height: shadowSpace) case .All: sizeOffset = CGSize(width: 0, height: 0) case .None: sizeOffset = CGSize.zero } self.layer.cornerRadius = self.frame.size.height / 2 self.layer.masksToBounds = true; self.layer.shadowColor = color.cgColor self.layer.shadowOpacity = opacity self.layer.shadowOffset = sizeOffset self.layer.shadowRadius = radius self.layer.masksToBounds = false self.layer.shadowPath = UIBezierPath(roundedRect:self.bounds, cornerRadius:self.layer.cornerRadius).cgPath } } enum AIEdge:Int { case Top, Left, Bottom, Right, Top_Left, Top_Right, Bottom_Left, Bottom_Right, All, None }

最后,应用带有角半径的阴影,如下所示:

viewRounded.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15)

结果图

更新:如果您没有看到预期的输出,请尝试从主线程调用扩展方法,这肯定会起作用!

DispatchQueue.main.async { viewRounded.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15) }
    

0
投票
UIButton 扩展

许多人建议使用

UIButton

 的自定义类,这完全没问题。万一你像我一样想要延期,这里有一个。 
用 Swift 5 编写。

extension UIButton { /// Adds a shadow to the button, with a corner radius /// - Parameters: /// - corner: The corner radius to apply to the shadow and button /// - color: The color of the shaodw /// - opacity: The opacity of the shadow /// - offset: The offset of the shadow /// - radius: The radius of the shadow func addShadow(corner: CGFloat = 20, color: UIColor = .black, opacity: Float = 0.3, offset: CGSize = CGSize(width: 0, height: 5), radius: CGFloat = 5) { let shadowLayer = CAShapeLayer() layer.cornerRadius = corner shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: corner).cgPath shadowLayer.fillColor = UIColor.clear.cgColor shadowLayer.shadowColor = color.cgColor shadowLayer.shadowPath = shadowLayer.path shadowLayer.shadowOffset = offset shadowLayer.shadowOpacity = opacity shadowLayer.shadowRadius = radius layer.insertSublayer(shadowLayer, at: 0) } }
    

0
投票
对于那些正在搜索带有阴影的圆形图像的人,您可以使用新的

填充按钮与胶囊cornerStyle并应用阴影与代码之后,您也可以尝试使用菜单中不同的cornerStyle尺寸:

backButton.layer.shadowOpacity = 1.0 backButton.layer.shadowOffset = CGSize(width: 1, height: 1) backButton.layer.shadowColor = UIColor.black.cgColor backButton.layer.masksToBounds = false


-1
投票
如果有人需要在 Swift 3.0 中为

rounded按钮添加阴影,这里是一个很好的方法。

func addShadowForRoundedButton(view: UIView, button: UIButton, shadowColor: UIColor, shadowOffset: CGSize, opacity: Float = 1) { let shadowView = UIView() shadowView.backgroundColor = shadowColor shadowView.layer.opacity = opacity shadowView.layer.cornerRadius = button.bounds.size.width / 2 shadowView.frame = CGRect(origin: CGPoint(x: button.frame.origin.x + shadowOffset.width, y: button.frame.origin.y + shadowOffset.height), size: CGSize(width: button.bouds.width, height: button.bounds.height)) self.view.addSubview(shadowView) view.bringSubview(toFront: button) }

func viewDidLayoutSubviews()

中使用此方法如下:

override func viewDidLayoutSubviews() { addShadowForRoundedButton(view: self.view, button: button, shadowColor: .black, shadowOffset: CGSize(width: 2, height: 2), opacity: 0.5) }

该方法的效果是:


-1
投票
您可以创建一个协议并使其符合您的 UIView、UIButton、Cell 或任何您想要的内容:

protocol RoundedShadowable: class { var shadowLayer: CAShapeLayer? { get set } var layer: CALayer { get } var bounds: CGRect { get } } ​ extension RoundedShadowable { func applyShadowOnce(withCornerRadius cornerRadius: CGFloat, andFillColor fillColor: UIColor) { if self.shadowLayer == nil { let shadowLayer = CAShapeLayer() shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath shadowLayer.fillColor = fillColor.cgColor shadowLayer.shadowColor = UIColor.black.cgColor shadowLayer.shadowPath = shadowLayer.path shadowLayer.shadowOffset = CGSize(width: 0.0, height: 2.0) shadowLayer.shadowOpacity = 0.2 shadowLayer.shadowRadius = 3 self.layer.insertSublayer(shadowLayer, at: 0) self.shadowLayer = shadowLayer } } } ​ class RoundShadowView: UIView, RoundedShadowable { var shadowLayer: CAShapeLayer? private let cornerRadius: CGFloat private let fillColor: UIColor init(cornerRadius: CGFloat, fillColor: UIColor) { self.cornerRadius = cornerRadius self.fillColor = fillColor super.init(frame: .zero) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() self.applyShadowOnce(withCornerRadius: self.cornerRadius, andFillColor: self.fillColor) } } ​ class RoundShadowButton: UIButton, RoundedShadowable { var shadowLayer: CAShapeLayer? private let cornerRadius: CGFloat private let fillColor: UIColor init(cornerRadius: CGFloat, fillColor: UIColor) { self.cornerRadius = cornerRadius self.fillColor = fillColor super.init(frame: .zero) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() self.applyShadowOnce(withCornerRadius: self.cornerRadius, andFillColor: self.fillColor) } }
    
© www.soinside.com 2019 - 2024. All rights reserved.