如何从 UIBezierPath 创建 IBDesignable 自定义 UIView?

Posted

技术标签:

【中文标题】如何从 UIBezierPath 创建 IBDesignable 自定义 UIView?【英文标题】:How to create an IBDesignable custom UIView from a UIBezierPath? 【发布时间】:2018-09-14 00:38:28 【问题描述】:

我想塑造一个 UIView 并能够在 Interface Builder 中看到它的形状,当我将以下类设置为我的 UIView 时,它无法构建,我不确定错误在哪里。

@IBDesignable
class CircleExampleView: UIView 

    override func layoutSubviews() 
        setupMask()
    

    func setupMask() 

        let path = makePath()

        // mask the whole view to that shape
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
    

    private func makePath() -> UIBezierPath 

        //// Oval Drawing
        let ovalPath = UIBezierPath(ovalIn: CGRect(x: 11, y: 12, width: 30, height: 30))
        UIColor.gray.setFill()
        ovalPath.fill()

        return ovalPath
    

【问题讨论】:

【参考方案1】:

几个观察:

    1234563使用CAShapeLayer时,应改为设置CAShapeLayerfillColor

    您正在使用CAShapeLayer 设置视图的掩码。如果您的UIView 没有可识别的backgroundColor,您将看不到任何内容。

    如果您将视图的背景颜色设置为蓝色,如下所示,您的遮罩将在遮罩允许的任何位置(在路径的椭圆中)显示蓝色背景。

    你已经实现了layoutSubviews。通常,只有当您在此处执行取决于视图的bounds 的操作时,您才会这样做。例如,下面是椭圆路径基于视图的bounds 的再现:

    @IBDesignable
    class CircleView: UIView 
        override func layoutSubviews() 
            super.layoutSubviews()
            setupMask()
        
    
        private func setupMask() 
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            mask.fillColor = UIColor.gray.cgColor
            layer.mask = mask
        
    
        private var path: UIBezierPath 
            return UIBezierPath(ovalIn: bounds)
        
    
    

    正如 E. Coms 所说,如果你覆盖 layoutSubviews,你真的应该调用 super 实现。这并不重要,因为默认实现实际上什么都不做,但这是最佳实践。例如。如果您稍后将该类更改为其他 UIView 子类的子类,您就不必重新访问所有这些覆盖。

    如果您有可设计的视图,建议将其放在单独的目标中。这样,故事板中视图的渲染不依赖于主项目中可能正在进行的任何工作。只要 designables 目标(通常是您的主要目标的名称,带有Kit 后缀)可以构建,就可以渲染 designable 视图。

例如,这里是您的可设计视图的再现,在一个单独的框架目标中,并在故事板中使用,其中相关视图具有蓝色 backgroundColor


对于它的价值,我认为必须遮盖以显示椭圆形内的背景颜色非常令人困惑。应用开发者必须设置“背景”颜色才能设置椭圆内的内容,而不是背景。

我可能会删除“掩码”逻辑,而是为可设计视图提供一个可检查的属性 fillColor,然后使用 fillColor 添加一个 CAShapeLayer 作为子层:

@IBDesignable
class CircleView: UIView 

    private var shapeLayer = CAShapeLayer()

    @IBInspectable var fillColor: UIColor = .blue 
        didSet 
            shapeLayer.fillColor = fillColor.cgColor
        
    

    override init(frame: CGRect = .zero) 
        super.init(frame: frame)

        configure()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)

        configure()
    

    private func configure() 
        shapeLayer.fillColor = fillColor.cgColor
        layer.addSublayer(shapeLayer)
    

    override func layoutSubviews() 
        super.layoutSubviews()

        shapeLayer.path = UIBezierPath(ovalIn: bounds).cgPath
    

这完成了同样的事情,但我认为填充颜色与背景颜色的区别更直观。但是你可能有其他原因使用遮罩方法,但只要确保你这样做,在它被遮罩后你有一些东西要显示(例如背景颜色或你正在渲染的其他东西)。

【讨论】:

【参考方案2】:

请添加“super.layoutSubviews()”

override func layoutSubviews() 
    setupMask()
    super.layoutSubviews()

通过这种方式,您的设计视图将是“最新的”。

【讨论】:

以上是关于如何从 UIBezierPath 创建 IBDesignable 自定义 UIView?的主要内容,如果未能解决你的问题,请参考以下文章

如何以给定的顺序动画/填充 UIBezierPath?

计算 UIBezierPath 曲线控制点

为 UIBezierPath 着色

iOS:从 UIBezierPath 创建图像剪切路径

如何使 UIBezierPath 从中心填充

如何使用 UIBezierPath 创建带有数据点的曲线图?