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

标签 ios swift uibezierpath ibdesignable

我想要塑造一个 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. 您正在调用setFill接下来是 fill ,但只有在绘制到图形上下文中时才可以这样做(例如,在 draw(_:) 中)。使用时CAShapeLayer ,您应该设置 fillColor CAShapeLayer的.

  2. 您正在使用CAShapeLayer设置您的 View 的 mask 。如果您UIView没有明显的backgroundColor ,您将看不到任何东西。

    如果将 View 的背景颜色设置为蓝色,如下所示,则蒙版将在蒙版允许的任何位置(在路径的椭圆形中)显示蓝色背景。

  3. 您已实现 layoutSubviews 。通常,只有当您在这里做的事情取决于 bounds 时,您才会这样做。的 View 。例如,这是一个椭圆形路径基于 bounds 的再现。 View :

    @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)
        }
    }
    
  4. 正如 E. Coms 所说,如果您覆盖 layoutSubviews ,你真的应该调用 super执行。这并不重要,因为默认实现实际上不执行任何操作,但这是最佳实践。例如。如果您后来将此类更改为其他类的子类 UIView子类,您不想重新访问所有这些重写。

  5. 如果您有可设计的 View ,建议将其放在单独的目标中。这样, Storyboard中 View 的呈现就不会依赖于主项目中可能正在进行的任何工作。只要可以构建可设计目标(通常是带有 Kit 后缀的主目标名称),就可以渲染可设计 View 。

例如,这里是可设计 View 的再现,在单独的框架目标中,并在 Storyboard 中使用,其中相关 View 具有蓝色 backgroundColor :

enter image description here


就其值(value)而言,我认为必须遮盖以显示椭圆形内的背景颜色非常令人困惑。应用开发人员必须设置“背景”颜色才能设置椭圆形内部的内容,但不能设置背景。

我可能会删除“掩码”逻辑,并为可设计 View 提供可检查的属性,fillColor ,只需添加 CAShapeLayer作为子层,使用 fillColor :

@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
    }
}

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

关于ios - 如何从 UIBezierPath 创建 IBDesignable 自定义 UIView?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52323491/

相关文章:

ios - 如何从 RAM 内存中清除图像

ios - CABasicAnimation CALayer 路径动画方向

ios - 如何计算弯曲的 SKSpriteNode 的 "center"点?

ios - UICollectionView 在边界更改时使布局无效

ios - 如何在 ios 中以编程方式设置 UICollectionView 的单元格大小

ios - DeviceAgent enter_text failed : Timed out after waiting 1. 0s for KeyEventCompleted 发送事件后

swift - 协议(protocol)只能用作通用约束

ios - UISearchBar 范围空白

ios - 使用 UIButton 实现 UIDatePicker 和 UIPickerView

ios - 在 Swift 中绘制工件