UIView带有圆角和阴影,但是在角落处不剪切子视图。

8
以下是自定义 Card View 的代码。问题在于,当我在界面构建器中添加子视图时,它不会将圆角应用于子视图。大部分情况下,我可以通过使子视图具有清晰的背景色来解决这个问题,但我在使用 UIImageView 时遇到了困难。当我将其添加到卡片中时,它会以尖角结束,而我无法修复它。
这里提供了各种解决方案,建议添加第二层以显示阴影。我尝试过这样做,但仍无法按预期工作。我想实现的是一个带有圆角、投影和添加任何子视图(如 UIImageView)应该也保持圆角而不是尖出的视图。
我尝试了各种设置,包括 layer.masksToBounds 和 self.clipsToBounds,但似乎总是得到具有圆角的子视图但没有阴影或可见阴影和未剪切的视图。
@IBDesignable class CardView: UIView {

    @IBInspectable dynamic var cornerRadius: CGFloat = 6
    @IBInspectable dynamic var shadowOffsetWidth: Int = 2
    @IBInspectable dynamic var shadowOffsetHeight: Int = 2
    @IBInspectable dynamic var shadowColor: UIColor? = UIColor(netHex: 0x333333)
    @IBInspectable dynamic var shadowOpacity: Float = 0.5

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    override func layoutSubviews() {
        commonInit()
    }

    override func prepareForInterfaceBuilder() {
        commonInit()
    }

    func commonInit() {

        layer.cornerRadius = cornerRadius
        let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
        layer.masksToBounds = false

        layer.shadowColor = shadowColor?.cgColor
        layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
        layer.shadowOpacity = shadowOpacity
        layer.shadowPath = shadowPath.cgPath

        // This was how I tried to add a seperate shadow layer
//        let shadowView = UIView(frame: self.frame)
//        shadowView.layer.shadowColor = shadowColor?.cgColor
//        shadowView.layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
//        shadowView.layer.shadowOpacity = shadowOpacity
//        shadowView.layer.shadowPath = shadowPath.cgPath
//        shadowView.layer.masksToBounds = false
//
//        self.addSubview(shadowView)

    }

}
2个回答

8
你尝试实现第二个视图来处理阴影的方法几乎正确,只是没有保持正确的顺序。
你的CardView类已经处理了显示阴影的功能。将该视图保持不变,而是添加一个名为“ContentView”的UIView作为其子视图。该内容视图具有与您的CardView相同的框架和圆角半径。
在"ContentView"上,您不需要处理任何阴影工作。相反,将其层的masksToBounds属性设置为true。现在将要显示在Card中的所有内容添加到“ContentView”中,它应该正确地裁剪。
func commonInit() {

    layer.cornerRadius = cornerRadius
    let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
    layer.masksToBounds = false

    layer.shadowColor = shadowColor?.cgColor
    layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
    layer.shadowOpacity = shadowOpacity
    layer.shadowPath = shadowPath.cgPath

    let contentView = UIView()
    contentView.frame = self.frame
    contentView.layer.cornerRadius = cornerRadius
    contentView.layer.masksToBounds = true

    // any content you add should now be added to the contentView:
    // contentView.addSubview(aView)
}

我正在尝试设置以下内容: contentView.frame = self.bounds contentView.layer.cornerRadius = cornerRadius contentView.layer.masksToBounds = true contentView.backgroundColor = .red self.insertSubview(contentView, at: 0)我不得不将frame设置为bounds(否则contentView.frame.origin偏移等于cardView在屏幕上的位置)。我将其设置为红色以便查看。如果我添加子视图,它会出现在界面构建器中的所有内容之上。如果我将其插入到索引1,则可以看到它,但子视图仍然似乎添加到卡片视图中。 - StartPlayer
如果您正在使用界面构建器,则将内容视图添加到CardView内,并通过“@IBOutlet”属性连接它们。从那里开始,您可以对内容视图进行相同的修改(例如corner RadiusmasksToBounds),而无需初始化其框架。然后,您可以将所有内容添加到界面构建器中的contentView子视图中。 - TomQDRS
这是否意味着我需要为应用程序中的每个cardView都这样做(有很多,我一直试图避免逐个查看所有内容),我能否使用nib重新创建cardView,并添加contentView以此方式进行一次操作。这样会有效吗? - StartPlayer
1
我将尝试制作一个带有两个视图的nib,其中一个带有阴影效果,另一个子视图则剪裁到边界。这不是适合于表格视图的东西,因为cardView是我在许多不同地方使用的UI特性,包括tableViewCells内部。 - StartPlayer
这种方法的问题在于,它无法与Interface Builder一起使用,除非您覆盖addSubview()以将子视图作为contentView的子项添加。但是,这会导致布局约束出现问题:您在IB中设置了一个子视图和父视图之间的约束,但实际上子视图是containerView的子项,因此在运行时会出现布局错误。 - Nicolas Miari
显示剩余4条评论

1

furthermore, you can specific corners.

layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接