Swift:制作类似 UIView 草图的轮廓

אור*_*hpt 2 outline uikit uiview swift

我想让 UIView 的轮廓看起来像有人画的那样“波浪形”。

我有来自 PowerPoint 的这个示例,它允许这样做(应该适用于任何尺寸和圆角半径):

在此处输入图片说明

目前这是我所拥有的:

myView.layer.borderWidth = 10
myView.layer.borderColor = UIColor.blue.cgColor
myView.layer.cornerRadius = 5 // Optional
Run Code Online (Sandbox Code Playgroud)

谢谢

Don*_*Mag 5

您可以使用UIBezierPath四边形曲线、直线、圆弧等的组合来创建“波浪”线。

我们将从一条简单的线开始,即视图宽度的四分之一:

在此处输入图片说明

我们的路径将包括:

  • 搬去 0,0
  • 添加行到 80,0

如果我们将其更改为四边形曲线:

在此处输入图片说明

现在我们正在做:

  • 搬去 0,0
  • 将四边形曲线添加到80,0控制点40,40

如果我们以另一种方式添加另一个四边形曲线:

在此处输入图片说明

现在我们正在做:

  • 搬去 0,0
  • 将四边形曲线添加到80,0控制点40,40
  • 将四边形曲线添加到160,0控制点120,-40

我们可以扩展视图的宽度:

在此处输入图片说明

当然,这看起来不像您的“草图”目标,所以让我们将控制点偏移量从 40 更改为 2:

在此处输入图片说明

现在它看起来更像是一条手绘的“草图”线。

但是,它太均匀了,并且部分超出了视图的边界,所以让我们将其插入 8 点,而不是四个 25% 的段,我们将使用(例如)五个这些宽度的段:

0.15, 0.2, 0.2, 0.27, 0.18
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

如果我们采用相同的方法从右侧向下,从底部返回,再从左侧向上,我们可以得到:

在此处输入图片说明

下面是一些生成该视图的示例代码:

class SketchBorderView: UIView {
    
    let borderLayer: CAShapeLayer = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = UIColor.blue.cgColor
        layer.addSublayer(borderLayer)
        backgroundColor = .yellow
    }
    override func layoutSubviews() {
        
        let incrementVals: [CGFloat] = [
            0.15, 0.2, 0.2, 0.27, 0.18,
        ]
        let lineOffsets: [[CGFloat]] = [
            [ 1.0, -2.0],
            [-1.0,  2.0],
            [-1.0, -2.0],
            [ 1.0,  2.0],
            [ 0.0, -2.0],
        ]

        let pth: UIBezierPath = UIBezierPath()
        
        // inset bounds by 8-pts so we can draw the "wavy border"
        //  inside our bounds
        let r: CGRect = bounds.insetBy(dx: 8.0, dy: 8.0)
        
        var ptDest: CGPoint = .zero
        var ptControl: CGPoint = .zero

        // start at top-left
        ptDest = r.origin
        pth.move(to: ptDest)

        // we're at top-left
        for i in 0..<incrementVals.count {

            ptDest.x += r.width * incrementVals[i]
            ptDest.y = r.minY + lineOffsets[i][0]

            ptControl.x = pth.currentPoint.x + ((ptDest.x - pth.currentPoint.x) * 0.5)
            ptControl.y = r.minY + lineOffsets[i][1]

            pth.addQuadCurve(to: ptDest, controlPoint: ptControl)

        }
        
        // now we're at top-right
        for i in 0..<incrementVals.count {
            
            ptDest.y += r.height * incrementVals[i]
            ptDest.x = r.maxX + lineOffsets[i][0]
            
            ptControl.y = pth.currentPoint.y + ((ptDest.y - pth.currentPoint.y) * 0.5)
            ptControl.x = r.maxX + lineOffsets[i][1]
            
            pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
            
        }
        
        // now we're at bottom-right
        for i in 0..<incrementVals.count {
            
            ptDest.x -= r.width * incrementVals[i]
            ptDest.y = r.maxY + lineOffsets[i][0]
            
            ptControl.x = pth.currentPoint.x - ((pth.currentPoint.x - ptDest.x) * 0.5)
            ptControl.y = r.maxY + lineOffsets[i][1]
            
            pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
            
        }
        
        // now we're at bottom-left
        for i in 0..<incrementVals.count {
            
            ptDest.y -= r.height * incrementVals[i]
            ptDest.x = r.minX + lineOffsets[i][0]
            
            ptControl.y = pth.currentPoint.y - ((pth.currentPoint.y - ptDest.y) * 0.5)
            ptControl.x = r.minX + lineOffsets[i][1]
            
            pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
            
        }

        borderLayer.path = pth.cgPath
    }
    
}
Run Code Online (Sandbox Code Playgroud)

和一个示例控制器:

class SketchTestVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let v = SketchBorderView()
        v.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(v)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            v.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            v.centerYAnchor.constraint(equalTo: g.centerYAnchor),
            v.widthAnchor.constraint(equalToConstant: 320.0),
            v.heightAnchor.constraint(equalTo: v.widthAnchor),
        ])
    }
    
}
Run Code Online (Sandbox Code Playgroud)

但是,使用该代码,我们仍然有太多的一致性,因此在实际使用中,我们希望将段数、段宽度和控制点偏移量随机化。

当然,要获得“圆角矩形”,您需要在拐角处添加圆弧。

不过,我希望这能让你顺利上路。