【问题标题】:Draw Drop Shadow Outside UIView在 UIView 外绘制阴影
【发布时间】:2016-11-14 19:06:16
【问题描述】:

背景

我有一个 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

结果

这会导致以下结果:

...虽然我不想看到这样的视图下方的阴影:

问题

如何只在视图外绘制阴影,使其在其下方不可见?

提前致谢!

【问题讨论】:

  • 您好,我不确定这是否是您正在寻找的,但也许在 Photoshop 中创建图像可以满足您的需求?
  • @LinkOpenheim 这是我作为解决方法所做的,但如果可能的话,我宁愿以编程方式创建它。
  • 当然,我知道的最好的方法是为阴影创建一个图层蒙版。你知道怎么做吗?
  • 不完全。您介意展示示例代码吗?

标签: ios swift shadow dropshadow


【解决方案1】:

这是从此处找到的帖子中改编而来的。我不知道你的图层的细节,所以我创建了一个以屏幕中间为中心的圆圈。您可以轻松地将圆圈替换为图层的规格,我相信这会纠正您的问题。如有任何进一步的说明,请随时发表评论! 导入 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
}
}

照片

【讨论】:

  • 如何添加投影?感觉你误解了我的问题:D
  • 这不会添加阴影,这会消除您视图下方的阴影。如果我正确理解您的问题,您希望在您的视图周围有一个阴影,而不是在它下方?
  • 我现在明白了,我确实误解了你的问题。抱歉,让我换一个解决方案
  • 没错。我希望投影在视图之外可见,但不在视图下方。
【解决方案2】:

这可能比它需要的稍微复杂一些,但这里有一个解决方案。

使用以下方法扩展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最早使用这个方法来得到上述结果。


这是背景视图的代码,以防万一有人想在 Playground 中进行测试:

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)

【讨论】:

  • 看起来不错,但对我来说 this is the result.
  • 抱歉,应该澄清一下:superview 参数需要“shadowView”从中获取其上下文的视图。所以如果你想让它模仿名为backgroundView的背景,你需要将它传递给方法:shadowView.dropShadow(backgroundView)
  • “模仿”到底是什么意思?所以,我有一个带有圆角和不透明度的白色视图,我想要有阴影。它的超级视图只是view,所以我将view 作为参数传递。不知何故,这是不正确的......
  • 刚刚添加了一个我用于该确切结果的示例背景。我所说的“模仿”是指该方法需要带有图像或渐变的背景或其他任何东西来从其上下文中创建图像。
  • 在 Playground 中试一试——先复制背景,然后是扩展,然后是使用示例。这应该稍微澄清一下,否则我们可能应该将其移至聊天:)
【解决方案3】:

我通常使用视图组合来解决这种情况。我没有在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

这种效果在背景模糊的情况下看起来非常好。

【讨论】:

  • 惊人的@kanobius 这是唯一对我有用的答案。
猜你喜欢
  • 2012-01-09
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 2010-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多