具有圆角和阴影的视图

在过去的几年中,移动和网络应用程序的总体设计模式一直持平。 借助iOS 7和Material Design,谷歌和苹果公司(紧随Windows 8的足迹,让我们在这里成为现实)宣布了崭新的设计模式。

平面UI非常棒。 它删除了看起来确实有点过时2000的时髦3D UI元素。

但是,随着iOS 11的发布,苹果似乎正在从更加突出的纯平UI转移到阴影和其他与深度相关的效果上。

它仍然是非常平坦的外观,只是带有一些用于指示用户如何与UI交互的附加信息。

上图中需要注意的一点是,单元格既有圆角,又有阴影。

为了在CALayer上圆角化,您需要执行以下操作。

 扩展名CALayer {
     func roundCorners(半径:CGFloat){
         self.cornerRadius =半径
     } 
 } 

这会将拐角半径应用于图层— 将拐角半径应用于背景颜色和边框。 这很重要,因为如果图层中有任何 contents则需要将masksToBounds设置为true ,以使拐角半径剪切内容。

为了给CALayer添加阴影,只需要多几行代码。

 扩展名CALayer {
     func addShadow(){
         self.shadowOffset = .zero
         self.shadowOpacity = 0.2
         self.shadowRadius = 10
         self.shadowColor = UIColor.black.cgColor
         self.masksToBounds =假
     }
 } 

支持视图的图层具有内容之前,在视图上同时具有阴影和圆角不是问题(范围可以从图像到其他非平凡的视图元素)。 如上所述,如果存在图层的contents ,则需要将masksToBounds设置为true才能应用圆角。 剪辑内容和显示阴影是不能同时存在的两种状态。

这是有道理的。 如果要在视图上放置阴影,则在视图上不能使用遮罩将其裁剪到边界,因为阴影会显示在视图边界之外。 要剪辑带有圆角的内容,您需要执行相反的操作。

因此,添加阴影并圆化视图的角点并不像听起来那样琐碎。

有一些易于实现的解决方案。 一种解决方案是使用容器视图封装带有圆角的子视图,而基础容器视图则具有阴影。 这很棒,直到您希望在应用程序中的多个视图中具有阴影和圆角。

拥有某种不影响视图层次结构的辅助方法真的很棒。 当然,构建一个可以模拟视图层次结构的扩展是可能的,但是可能很乏味。 幸运的是,在视图层进行操作要容易得多。

CALayer具有方便的属性contents,它表示图层中的所有内容。 该值通常为nil,但是在UIImageView它是图像,在其他情况下可以为非nil。

一层的内容可以转移到另一层,基础内容也不会改变。 这确实很棒,因为这是我们想要做的-移动图层的内容,以便我们可以应用拐角半径,并在原始图层上保留阴影。

 如果让content = self.contents {
     self.contents = nil让contentLayer = CALayer()
     contentLayer.name = Constants.contentLayerName
     contentLayer.contents =内容
     contentLayer.frame =界限
     contentLayer.cornerRadius = cornerRadius
     contentLayer.masksToBounds = true insertSublayer(contentLayer,at:0)
 } 

在这里,我们将当前层的内容移动到内容层,应用拐角半径,将内容裁剪到边界,然后将内容层作为第一个子层插入。 这是与上述UIView方法非常相似的方法,但是它不操纵视图布局。

如果我们在当前图层masksToBounds设置为false并添加阴影,则可以同时获得阴影和拐角半径,从而获得理想的效果。

不幸的是,如果有任何子层填充父层的边界,那么这些子层将不会被相应地裁剪。

为了达到此效果,我们需要手动执行拐角半径。

  sublayers?.filter {$ 0.frame.equalTo(self.bounds)}
     .forEach {$ 0.roundCorners(radius:self.cornerRadius)} 

这将在当前层层次结构中的任何子层上设置所需的拐角半径。

总的来说,这具有圆角化UIView并显示阴影的理想效果。 以下功能结合了前面两种方法。

 私人功能addShadowWithRoundedCorners(){
    如果让content = self.contents {masksToBounds = false
         sublayers?.filter {$ 0.frame.equalTo(self.bounds)}
             .forEach {$ 0.roundCorners(radius:self.cornerRadius)} self.contents = nil如果let sublayer = sublayers?。首先,
             sublayer.name == Constants.contentLayerName {
            
             sublayer.removeFromSuperlayer()
        让contentLayer = CALayer()
         contentLayer.name = Constants.contentLayerName
         contentLayer.contents =内容
         contentLayer.frame =界限
         contentLayer.cornerRadius = cornerRadius
         contentLayer.masksToBounds = true insertSublayer(contentLayer,at:0)
     }
 } 

这里有一些其他逻辑-主要是考虑到不添加冗余内容层。

添加内容层后,我们将为内容层分配一个指定的名称。 在随后调用此方法时,也许如果正在layoutSubviews,中调用它layoutSubviews,那么我们将检查内容层是否已经存在。 如果是这样,它将被删除。 然后添加具有当前内容的新内容层。

如果该图层没有任何内容,则实际上什么也不会发生。 在这种情况下,只需应用拐角半径即可。

现在,在roundCornersaddShadow函数中,我们可以调用addShadowWithRoundedCorners ,并且我们的图层将根据需要应用适当的更改。

 扩展名CALayer {
     func addShadow(){
         self.shadowOffset = .zero
         self.shadowOpacity = 0.2
         self.shadowRadius = 10
         self.shadowColor = UIColor.black.cgColor
         self.masksToBounds = false,如果cornerRadius!= 0 {
             addShadowWithRoundedCorners()
         }
     } func roundCorners(radius:CGFloat){
         self.cornerRadius =半径,如果shadowOpacity!= 0 {
             addShadowWithRoundedCorners()
         }    
     }
 } 

这是一种很好的方法,可以抽象出在任何UIViewCALayer,上启用阴影和圆角的逻辑CALayer,并且可以将其扩展到圆角之外的任何类型的蒙版。

希望您喜欢这篇文章,并随时留下任何反馈!