Swift - Problems with corner radius and drop shadow

前端 未结 14 1371
失恋的感觉
失恋的感觉 2020-11-28 18:35

I\'m trying to create a button with rounded corners and a drop shadow. No matter how I switch up, the button will not display correctly. I\

相关标签:
14条回答
  • 2020-11-28 18:36

    To improve PiterPan's answer and show a real shadow (not just a background with no blur) with a circular button in 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 讨论(0)
  • 2020-11-28 18:41

    The following Swift 5 / iOS 12 code shows how to set a subclass of UIButton that allows to create instances with rounded corners and shadow around it:

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

    According to your needs, you may add a UIButton in your Storyboard and set its class to CustomButton or you may create an instance of CustomButton programmatically. The following UIViewController implementation shows how to create and use a CustomButton instance programmatically:

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

    The previous code produces the image below in the iPhone simulator:

    0 讨论(0)
  • 2020-11-28 18:42

    An alternative way to get more usable and consistent button.

    Swift 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
    

    Swift 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
    
    0 讨论(0)
  • 2020-11-28 18:42

    Here is the solution that will work!

    
    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
    }
    

    Finally, to apply shadow with corner radius call as per below:

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

    Result Image

    UPDATE: If you don't see the expected output then try calling the extension method from Main Thread, that will work for sure!

    DispatchQueue.main.async {
        viewRounded.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15)
    }
    
    0 讨论(0)
  • 2020-11-28 18:44

    Corner Radius with Shadow

    Short and simple way !!!!!

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

    Use of code

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

    No need maskToBound

    Please verify clipsToBounds is false.

    OUTPUT

    0 讨论(0)
  • 2020-11-28 18:45

    Extension to drop shadow and corner radius

    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 讨论(0)
提交回复
热议问题