在 UIView 外绘制阴影
Draw Drop Shadow Outside UIView
背景
我有一个 UIView
具有以下属性:
- 阿尔法 = 1
- backgroundColor = 白色,不透明度为 0.35
- 圆角
- 投影
代码
这就是我创建投影的方式:(UIView
扩展名)
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.darkGrayColor().CGColor
self.layer.shadowOffset = CGSizeMake(0, 5)
self.layer.shadowOpacity = 0.35
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).CGPath
结果
结果如下:
...虽然我不想看到这样的视图下方的阴影:
问题
如何只在视图外绘制阴影,使其在视图下方不可见?
提前致谢!
这是从此处找到的 post 改编而来的。我不知道你图层的细节,所以我创建了一个以屏幕中间为中心的圆圈。您可以轻松地将圆圈替换为图层的规范,我相信这会解决您的问题。请随时评论任何进一步的说明!
导入 UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
createOverlay(view.bounds)
}
func createOverlay(frame : CGRect)
{
let overlayView = UIView(frame: frame)
overlayView.alpha = 0.6
overlayView.backgroundColor = UIColor.blackColor()
self.view.addSubview(overlayView)
let maskLayer = CAShapeLayer()
let path = CGPathCreateMutable()
let radius : CGFloat = 50.0
let xOffset : CGFloat = view.bounds.width / 2
let yOffset : CGFloat = view.bounds.height / 2
CGPathAddArc(path, nil, overlayView.frame.width - xOffset, yOffset, radius, 0.0, 2 * 3.14, false)
CGPathAddRect(path, nil, CGRectMake(0, 0, overlayView.frame.width, overlayView.frame.height))
maskLayer.backgroundColor = UIColor.blackColor().CGColor
maskLayer.path = path;
maskLayer.fillRule = kCAFillRuleEvenOdd
overlayView.layer.mask = maskLayer
overlayView.clipsToBounds = true
}
}
照片
这可能比需要的稍微复杂一些,但这是一个解决方案。
使用以下方法扩展 UIView
:
extension UIView {
// Note: the method needs the view from which the context is taken as an argument.
func dropShadow(superview: UIView) {
// Get context from superview
UIGraphicsBeginImageContext(self.bounds.size)
superview.drawViewHierarchyInRect(CGRect(x: -self.frame.minX, y: -self.frame.minY, width: superview.bounds.width, height: superview.bounds.height), afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Add a UIImageView with the image from the context as a subview
let imageView = UIImageView(frame: self.bounds)
imageView.image = image
imageView.layer.cornerRadius = self.layer.cornerRadius
imageView.clipsToBounds = true
self.addSubview(imageView)
// Bring the background color to the front, alternatively set it as UIColor(white: 1, alpha: 0.2)
let brighter = UIView(frame: self.bounds)
brighter.backgroundColor = self.backgroundColor ?? UIColor(white: 1, alpha: 0.2)
brighter.layer.cornerRadius = self.layer.cornerRadius
brighter.clipsToBounds = true
self.addSubview(brighter)
// Set the shadow
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.darkGrayColor().CGColor
self.layer.shadowOffset = CGSizeMake(0, 5)
self.layer.shadowOpacity = 0.35
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).CGPath
}
}
用法,考虑到背景视图被命名为view
:
let shadowView = UIView(frame: CGRect(x: 100, y: 100, width: 300, height: 200))
shadowView.layer.cornerRadius = 15.0
shadowView.dropShadow(view)
view.addSubview(shadowView)
结果如下所示:
注意: dropShadow
方法 不能 从 viewDidLoad
调用,因为这会导致问题图形上下文。所以,最早在viewWillAppear
使用这个方法就得到了上面的结果。
这是背景视图的代码,以防万一有人想在操场上进行测试:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 400))
view.backgroundColor = UIColor.clearColor()
let color1 = UIColor(hue: 0.39, saturation: 0.7, brightness: 1.0, alpha: 1.0).CGColor
let color2 = UIColor(hue: 0.51, saturation: 0.9, brightness: 0.6, alpha: 1.0).CGColor
let gradient = CAGradientLayer()
gradient.frame = view.frame
gradient.colors = [color1, color2]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
view.layer.insertSublayer(gradient, atIndex: 0)
我通常使用视图组合来解决这种情况。我没有在 target
视图中设置阴影,而是创建了一个 ShadowView
并将其放在 target
视图后面,具有相同的框架。
这样做,我们可以屏蔽阴影视图,以便只在其框架外绘制阴影。
阴影遮罩的代码是(上面的完整代码):
let maskLayer = CAShapeLayer()
let path = CGMutablePath()
path.addPath(UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets.zero), cornerRadius: CGFloat(cornerRadius)).cgPath)
path.addPath(UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets(top: -offset.height - radius*2, left: -offset.width - radius*2, bottom: -offset.height - radius*2, right: -offset.width - radius*2)), cornerRadius: CGFloat(cornerRadius)).cgPath)
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path;
maskLayer.fillRule = .evenOdd
self.layer.mask = maskLayer
结果如下:
此处提供完整的游乐场:
https://gist.github.com/llinardos/d1ede3b491efc8edac940c5ea8631c6f
这种效果在背景模糊的情况下看起来非常好。
背景
我有一个 UIView
具有以下属性:
- 阿尔法 = 1
- backgroundColor = 白色,不透明度为 0.35
- 圆角
- 投影
代码
这就是我创建投影的方式:(UIView
扩展名)
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.darkGrayColor().CGColor
self.layer.shadowOffset = CGSizeMake(0, 5)
self.layer.shadowOpacity = 0.35
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).CGPath
结果
结果如下:
...虽然我不想看到这样的视图下方的阴影:
问题
如何只在视图外绘制阴影,使其在视图下方不可见?
提前致谢!
这是从此处找到的 post 改编而来的。我不知道你图层的细节,所以我创建了一个以屏幕中间为中心的圆圈。您可以轻松地将圆圈替换为图层的规范,我相信这会解决您的问题。请随时评论任何进一步的说明! 导入 UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
createOverlay(view.bounds)
}
func createOverlay(frame : CGRect)
{
let overlayView = UIView(frame: frame)
overlayView.alpha = 0.6
overlayView.backgroundColor = UIColor.blackColor()
self.view.addSubview(overlayView)
let maskLayer = CAShapeLayer()
let path = CGPathCreateMutable()
let radius : CGFloat = 50.0
let xOffset : CGFloat = view.bounds.width / 2
let yOffset : CGFloat = view.bounds.height / 2
CGPathAddArc(path, nil, overlayView.frame.width - xOffset, yOffset, radius, 0.0, 2 * 3.14, false)
CGPathAddRect(path, nil, CGRectMake(0, 0, overlayView.frame.width, overlayView.frame.height))
maskLayer.backgroundColor = UIColor.blackColor().CGColor
maskLayer.path = path;
maskLayer.fillRule = kCAFillRuleEvenOdd
overlayView.layer.mask = maskLayer
overlayView.clipsToBounds = true
}
}
照片
这可能比需要的稍微复杂一些,但这是一个解决方案。
使用以下方法扩展 UIView
:
extension UIView {
// Note: the method needs the view from which the context is taken as an argument.
func dropShadow(superview: UIView) {
// Get context from superview
UIGraphicsBeginImageContext(self.bounds.size)
superview.drawViewHierarchyInRect(CGRect(x: -self.frame.minX, y: -self.frame.minY, width: superview.bounds.width, height: superview.bounds.height), afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Add a UIImageView with the image from the context as a subview
let imageView = UIImageView(frame: self.bounds)
imageView.image = image
imageView.layer.cornerRadius = self.layer.cornerRadius
imageView.clipsToBounds = true
self.addSubview(imageView)
// Bring the background color to the front, alternatively set it as UIColor(white: 1, alpha: 0.2)
let brighter = UIView(frame: self.bounds)
brighter.backgroundColor = self.backgroundColor ?? UIColor(white: 1, alpha: 0.2)
brighter.layer.cornerRadius = self.layer.cornerRadius
brighter.clipsToBounds = true
self.addSubview(brighter)
// Set the shadow
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.darkGrayColor().CGColor
self.layer.shadowOffset = CGSizeMake(0, 5)
self.layer.shadowOpacity = 0.35
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).CGPath
}
}
用法,考虑到背景视图被命名为view
:
let shadowView = UIView(frame: CGRect(x: 100, y: 100, width: 300, height: 200))
shadowView.layer.cornerRadius = 15.0
shadowView.dropShadow(view)
view.addSubview(shadowView)
结果如下所示:
注意: dropShadow
方法 不能 从 viewDidLoad
调用,因为这会导致问题图形上下文。所以,最早在viewWillAppear
使用这个方法就得到了上面的结果。
这是背景视图的代码,以防万一有人想在操场上进行测试:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 400))
view.backgroundColor = UIColor.clearColor()
let color1 = UIColor(hue: 0.39, saturation: 0.7, brightness: 1.0, alpha: 1.0).CGColor
let color2 = UIColor(hue: 0.51, saturation: 0.9, brightness: 0.6, alpha: 1.0).CGColor
let gradient = CAGradientLayer()
gradient.frame = view.frame
gradient.colors = [color1, color2]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
view.layer.insertSublayer(gradient, atIndex: 0)
我通常使用视图组合来解决这种情况。我没有在 target
视图中设置阴影,而是创建了一个 ShadowView
并将其放在 target
视图后面,具有相同的框架。
这样做,我们可以屏蔽阴影视图,以便只在其框架外绘制阴影。
阴影遮罩的代码是(上面的完整代码):
let maskLayer = CAShapeLayer()
let path = CGMutablePath()
path.addPath(UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets.zero), cornerRadius: CGFloat(cornerRadius)).cgPath)
path.addPath(UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets(top: -offset.height - radius*2, left: -offset.width - radius*2, bottom: -offset.height - radius*2, right: -offset.width - radius*2)), cornerRadius: CGFloat(cornerRadius)).cgPath)
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path;
maskLayer.fillRule = .evenOdd
self.layer.mask = maskLayer
结果如下:
此处提供完整的游乐场: https://gist.github.com/llinardos/d1ede3b491efc8edac940c5ea8631c6f
这种效果在背景模糊的情况下看起来非常好。