UIView上的多个阴影

最近,我遇到了在单个UIView上具有多个阴影的要求。 要求非常简单:

  • 具有1px宽度的外围阴影。
  • 像Material design中一样,具有一个可变的环境阴影以产生高程感。

预期结果如下:

我想使这种elevation通用,以便可以应用于任何视图。 事实证明,我在这段代码上的经历与我所期望的不一样。 在本文中,我将讨论用于获得设计人员所需内容的代码的演变。

乍一看,似乎我可以将layer.border用作外围阴影,但是它并没有产生预期的结果。 使用layer.border似乎要暗得多,并且没有像使用shadow那样得到的扩散:

为了使该方法通用,我在UIView创建了一个名为applyShadows的扩展,并添加了以下代码段中所示的代码。

第一次尝试—使用BezierPaths

第一次尝试是在view layer上使用UIBezierPath

直到我根据预期可以应用该标高的view尝试了这一切之后,一切似乎都运行良好。 如下图所示,高程代码未考虑对要应用高程的视图的子视图进行四舍五入的情况。 这导致UI损坏:

如上图所示,虽然父视图具有圆角和阴影,但subview没有圆角。

第三次尝试-阴影图层上的插图

为了解决舍入的问题,我不能使用clipsToBound因为它会导致阴影也被修剪。 作为一种解决方法,我想到了使用与cornerRadius相等的边插入量。

但是,此方法只能在某些特定情况下使用。 例如,如果subview是可滚动的,并且在滚动时期望接触父视图边界,则此方法将不起作用。 下图直观地显示了问题:

在上图中,黄色是父视图,青色是子视图。 这种方法更多的是破解而不是解决方案。 这隐藏了问题,而不是解决问题。

第4次尝试-遮罩子视图

UIView提供了一个称为mask的属性,该属性似乎是我在第三次尝试中遇到的问题的解决方案。 使用mask ,我们可以舍入子视图而不会在阴影上留下斑点:

通过这种尝试,我们解决了非四舍五入的子视图的问题,并且我们也没有任何其他插图。 尽管这种方法似乎可行,但这再次给可滚动子视图带来了问题:

在上面的gif中,蓝色是父视图的颜色,其中包含可滚动的子视图。 子视图有9个项目,但我们仅看到4个项目,因为其他项目被应用的蒙版隐藏了。

第5次尝试-使用阴影附加视图

因此,对于我不断clipToBounds的问题的解决方案很明确-我必须在视图上使用clipToBounds才能在拐角处获得所需的舍入。 这也意味着我不能再在有关视图上应用阴影。 但是,我仍然可以使用以下方法获得所需的外观:

  • 创建与当前框架相同的其他视图。
  • 使用子图层方法向新视图添加阴影。
  • 将新视图放置在当前视图的后面。
  • 在当前视图上设置clipsToBounds

就是这样。

奖金提示

  • 图层名称:您必须已经注意到我已经将layer.name设置为一个值perimeterShadow 。 这用作删除/调整图层大小的标识符,在这里我可以查询所有子图层以获得阴影图层。
  • 图层的背景颜色:如果未设置layer.backgroundColor ,则阴影不会显示。
  • 图层动画:您可以查询图层的动画以获取关键点, position以获取视图的durationtimingFunction 。 然后,可以使用该方法随着视图的大小调整阴影的大小,因为视图的大小由于方向的变化而改变。