Ich versuche eine Schaltfläche mit abgerundeten Ecken und einem Schlagschatten zu erstellen . Egal wie ich hochschalte, die Schaltfläche wird nicht richtig angezeigt. Ich habe es versucht masksToBounds = false
und masksToBounds = true
, aber entweder funktioniert der Eckenradius und der Schatten nicht oder der Schatten funktioniert und der Eckenradius schneidet die Ecken der Schaltfläche nicht ab.
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
}
}
drawRect
ist keine sehr gute Idee. Besser sie reinsteckeninitWithCoder
.Antworten:
Der folgende Swift 5 / iOS 12-Code zeigt, wie Sie eine Unterklasse festlegen
UIButton
, mit der Instanzen mit abgerundeten Ecken und Schatten erstellt werden können: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 } } }
Je nach Bedarf können Sie ein
UIButton
in Ihr Storyboard einfügen und dessen Klasse auf festlegenCustomButton
oder eine Instanz vonCustomButton
programmgesteuert erstellen . Die folgendeUIViewController
Implementierung zeigt, wie eineCustomButton
Instanz programmgesteuert erstellt und verwendet wird :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]) } }
Der vorherige Code erzeugt das folgende Bild im iPhone-Simulator:
quelle
masksToBounds = YES
abgerundete Ecken aufweisen muss, wird der Schatten dann nicht auch abgeschnitten?Meine benutzerdefinierte Schaltfläche mit einigen Schatten und abgerundeten Ecken verwende ich direkt in der,
Storyboard
ohne sie programmgesteuert berühren zu müssen .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 } }
quelle
layoutSubviews()
.Um Imanous Beitrag zu erweitern, können Sie die Schattenebene programmgesteuert zur benutzerdefinierten Schaltflächenklasse hinzufügen
@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) } }
quelle
Ein alternativer Weg, um benutzerfreundlichere und konsistentere Schaltflächen zu erhalten.
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
quelle
Dies wurde überarbeitet , um jede Ansicht zu unterstützen. Unterklassifizieren Sie Ihre Ansicht von dieser und es sollte abgerundete Ecken haben. Wenn Sie dieser Ansicht so etwas wie eine UIVisualEffectView als Unteransicht hinzufügen, müssen Sie wahrscheinlich dieselben abgerundeten Ecken für diese UIVisualEffectView verwenden, da sie sonst keine abgerundeten Ecken aufweist.
/// 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) } } }
quelle
Swift 5 & Keine Notwendigkeit von "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
quelle
UIBezierPath
ist aus Leistungsgründen, falls Sie diese bestimmte Ansicht wiederverwenden müssen.So verbessern Sie die Antwort von PiterPan und zeigen in Swift 3 einen echten Schatten (nicht nur einen Hintergrund ohne Unschärfe) mit einem kreisförmigen Knopf:
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) }
quelle
Hier ist die Lösung, die funktionieren wird!
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 }
Um schließlich Schatten mit Eckradius anzuwenden, rufen Sie wie folgt auf:
viewRounded.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15)
Ergebnisbild
UPDATE : Wenn Sie die erwartete Ausgabe nicht sehen, versuchen Sie, die Erweiterungsmethode vom Haupt-Thread aufzurufen. Das wird sicher funktionieren!
DispatchQueue.main.async { viewRounded.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15) }
quelle
Wenn jemand in Swift 3.0 abgerundeten Schaltflächen Schatten hinzufügen muss , ist dies eine gute Methode.
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) }
Verwenden Sie diese Methode
func viewDidLayoutSubviews()
wie folgt:override func viewDidLayoutSubviews() { addShadowForRoundedButton(view: self.view, button: button, shadowColor: .black, shadowOffset: CGSize(width: 2, height: 2), opacity: 0.5) }
Die Wirkung dieser Methode ist:
quelle
Erweiterung um Schlagschatten und Eckenradius
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) } }
quelle
Genaue Lösung für die Syntax 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 self.layer.insertSublayer(backgroundAndShadow, 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 = bounds colorAndShadow = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath } }
Beachten Sie, dass dies
UIButton
leider ganz anders ist alsUIView
in iOS.Im Allgemeinen sind Kombinationen von Schatten / Rundungen unter iOS ein echtes Problem. Ähnliche Lösungen:
https://stackoverflow.com/a/57465440/294884 - Bild + gerundet + Schatten
https://stackoverflow.com/a/41553784/294884 - Zwei-Ecken-Problem
https://stackoverflow.com/a/59092828/294884 - "Shadows + Hole" - oder "Glowbox" -Problem
https://stackoverflow.com/a/57400842/294884 - Das "Border AND Gap" -Problem
https://stackoverflow.com/a/57514286/294884 - Grundlegendes "Hinzufügen" Bezier
quelle
Sie können ein Protokoll erstellen und es an Ihre UIView, UIButton, Cell oder was auch immer Sie möchten anpassen:
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) } }
quelle
Mit CAShapeLayer und UIBezierPath können wir UIView und der daraus abgeleiteten Klasse wie UIButton, UIImageView, UILabel usw. problemlos Schatten- und Eckenradius hinzufügen.
Wir werden die UIView-Erweiterung hinzufügen. Der gute Punkt dabei ist, dass Sie nur eine Codezeile schreiben müssen, um dies zu erreichen.
Sie können auch eine bestimmte Ecke abrunden. Fügen Sie einfach die folgende UIView-Erweiterung in Ihr Projekt ein:
extension UIView { func addShadow(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) { let shadowLayer = CAShapeLayer() let size = CGSize(width: cornerRadius, height: cornerRadius) let cgPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1 shadowLayer.path = cgPath //2 shadowLayer.fillColor = fillColor.cgColor //3 shadowLayer.shadowColor = shadowColor.cgColor //4 shadowLayer.shadowPath = cgPath shadowLayer.shadowOffset = offSet //5 shadowLayer.shadowOpacity = opacity shadowLayer.shadowRadius = shadowRadius self.layer.addSublayer(shadowLayer) } }
Schreiben Sie nun einfach den folgenden Code, um Schatten und Eckenradius hinzuzufügen
self.myView.addShadow(shadowColor: .black, offSet: CGSize(width: 2.6, height: 2.6), opacity: 0.8, shadowRadius: 5.0, cornerRadius: 20.0, corners: [.topRight, .topLeft], fillColor: .red)
quelle
Eckradius mit Schatten
Kurzer und einfacher Weg !!!!!
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 } }
Verwendung von Code
btn.layer.applyCornerRadiusShadow(color: .black, alpha: 0.38, x: 0, y: 3, blur: 10, spread: 0, cornerRadiusValue: 24)
Bitte überprüfen Sie, ob clipsToBounds falsch ist.
AUSGABE
quelle