【问题标题】:iOS Draw Custom Shape using CAShapeLayeriOS 使用 CAShapeLayer 绘制自定义形状
【发布时间】:2018-05-24 04:49:09
【问题描述】:

我想画一个类似于下图的自定义形状。


  1. 倒圆角线
  2. 空心圆
  3. 接下来的另一行

我在 Android 中通过以下方式实现了这一点。

float radiusClear = halfWidth - strokeSize / 2f; // 1
canvas.drawRect(0, 0, width, radiusClear, rootPaint); // 2
canvas.drawCircle(0, radiusClear, radiusClear, clearPaint); // 3
canvas.drawCircle(width, radiusClear, radiusClear, clearPaint); // 4
canvas.drawLine(halfWidth, 0, halfWidth, halfHeight, rootPaint); // 5
canvas.drawLine(halfWidth, halfHeight, halfWidth, height, iconPaint); // 6
canvas.drawCircle(halfWidth, halfHeight, halfWidth, iconPaint); // 7
canvas.drawCircle(halfWidth, halfHeight, thirdWidth, clearPaint); // 8
  • 其中 (1) 计算距离。
  • (2) 在顶部画一个矩形

  • (3) (4) 绘制两个圆来清除矩形,使其看起来像两条弧线

  • 然后其余调用以类似方式绘制其余视图。

在 swift 上等效或更好的方法是什么?

【问题讨论】:

    标签: ios swift uiview uibezierpath cashapelayer


    【解决方案1】:

    我根据您的要求制作了快速代码。希望对你有帮助。

    //// Color Declarations
    let color = UIColor(red: 0.387, green: 0.416, blue: 0.718, alpha: 1.000)
    let color2 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
    let color3 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
    let color4 = UIColor(red: 0.300, green: 0.586, blue: 0.712, alpha: 1.000)
    
    //// Oval Drawing
    let ovalPath = UIBezierPath(ovalIn: CGRect(x: 41, y: 39, width: 20, height: 20))
    color.setStroke()
    ovalPath.lineWidth = 2.5
    ovalPath.stroke()
    
    
    //// Rectangle 2 Drawing
    let rectangle2Path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 100, height: 17))
    color4.setFill()
    rectangle2Path.fill()
    
    
    //// Bezier 2 Drawing
    let bezier2Path = UIBezierPath()
    bezier2Path.move(to: CGPoint(x: -6.5, y: 18.5))
    bezier2Path.addCurve(to: CGPoint(x: 41.76, y: 18.5), controlPoint1: CGPoint(x: 40.61, y: 18.5), controlPoint2: CGPoint(x: 41.76, y: 18.5))
    bezier2Path.addCurve(to: CGPoint(x: 47.5, y: 22.7), controlPoint1: CGPoint(x: 41.76, y: 18.5), controlPoint2: CGPoint(x: 47.5, y: 18.5))
    bezier2Path.addCurve(to: CGPoint(x: 47.5, y: 39.5), controlPoint1: CGPoint(x: 47.5, y: 26.9), controlPoint2: CGPoint(x: 47.5, y: 39.5))
    color3.setStroke()
    bezier2Path.lineWidth = 1
    bezier2Path.stroke()
    
    
    //// Bezier 3 Drawing
    let bezier3Path = UIBezierPath()
    bezier3Path.move(to: CGPoint(x: 100.5, y: 17.5))
    bezier3Path.addCurve(to: CGPoint(x: 58.5, y: 17.5), controlPoint1: CGPoint(x: 59.5, y: 17.5), controlPoint2: CGPoint(x: 58.5, y: 17.5))
    bezier3Path.addCurve(to: CGPoint(x: 55.5, y: 21.5), controlPoint1: CGPoint(x: 58.5, y: 17.5), controlPoint2: CGPoint(x: 55.5, y: 18.5))
    bezier3Path.addCurve(to: CGPoint(x: 55.5, y: 39.5), controlPoint1: CGPoint(x: 55.5, y: 24.5), controlPoint2: CGPoint(x: 55.5, y: 39.5))
    color2.setStroke()
    bezier3Path.lineWidth = 1
    bezier3Path.stroke()
    
    
    //// Rectangle Drawing
    let rectanglePath = UIBezierPath(rect: CGRect(x: 47, y: 59, width: 8, height: 41))
    color.setFill()
    rectanglePath.fill()
    

    【讨论】:

    • 谢谢!阿米特,你的代码对我帮助很大。终于可以解决了。
    【解决方案2】:
    //
    //  ConnectorView.swift
    //
    //  Created by harsh vishwakrama on 5/24/18.
    //
    
    import UIKit
    
    private let grayColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
    private let purpleColor = UIColor(red: 0.387, green: 0.416, blue: 0.718, alpha: 1.000)
    
    
    @IBDesignable
    class ConnectorView: UIView {
    
        var mode: Mode = .end{
            didSet{
                let width = bounds.width
                let height = bounds.height
                let halfWidth = bounds.width / 2
                let halfHeight = bounds.height / 2
                let thirdWidth = bounds.width / 3
                let strokeWidth = width / 5
                let midPoint = CGPoint(x: bounds.midX, y: bounds.midY)
    
                switch mode {
                case .start:
                    drawStart(width, thirdWidth, halfWidth, halfHeight, midPoint,strokeWidth)
                case .node:
                    drawNode(halfWidth, thirdWidth, halfHeight, midPoint,strokeWidth)
                case .end:
                    drawEnd(halfWidth, thirdWidth, halfHeight, midPoint,strokeWidth)
                case .only:
                    drawOnly(width, thirdWidth, halfWidth, halfHeight, strokeWidth, midPoint)
                }
                layoutSubviews()
            }
        }
    
    
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            clipsToBounds = true
        }
    
        enum Mode {
            case start, node, end, only
        }
    }
    extension ConnectorView{
        fileprivate func drawStart(_ width: CGFloat, _ thirdWidth: CGFloat, _ halfWidth: CGFloat, _ halfHeight: CGFloat, _ midPoint: CGPoint, _ strokeWidth: CGFloat) {
            layer.sublayers?.forEach{ layer in
                layer.removeFromSuperlayer()
            }
            let linePathTop = UIBezierPath()
            linePathTop.move(to: CGPoint(x: -width, y: -thirdWidth))
            linePathTop.addCurve(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth), controlPoint1: CGPoint(x: halfWidth, y: -thirdWidth ), controlPoint2: CGPoint(x: halfWidth, y: -thirdWidth))
            linePathTop.move(to: CGPoint(x: 2 * width, y: -thirdWidth))
            linePathTop.addCurve(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth), controlPoint1: CGPoint(x: halfWidth, y: -thirdWidth ), controlPoint2: CGPoint(x: halfWidth, y: -thirdWidth))
            linePathTop.move(to: CGPoint(x: 0, y: -thirdWidth))
            linePathTop.addLine(to: CGPoint(x: width, y: -thirdWidth))
            linePathTop.move(to: CGPoint(x: halfWidth, y: -thirdWidth))
            linePathTop.addLine(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth))
            linePathTop.close()
    
            let shapeLayerTop = CAShapeLayer()
            shapeLayerTop.path = linePathTop.cgPath
            shapeLayerTop.fillColor = UIColor.clear.cgColor
            shapeLayerTop.strokeColor = purpleColor.cgColor
            shapeLayerTop.lineWidth = strokeWidth
            layer.addSublayer(shapeLayerTop)
    
            let shapeLayerBottom = CAShapeLayer()
            let linePathBottom = UIBezierPath()
            linePathBottom.move(to: CGPoint(x: halfWidth, y: halfHeight + thirdWidth))
            linePathBottom.addLine(to: CGPoint(x: halfWidth, y: bounds.height))
            linePathBottom.close()
    
            shapeLayerBottom.path = linePathBottom.cgPath
            shapeLayerBottom.strokeColor = grayColor.cgColor
            shapeLayerBottom.fillColor = UIColor.clear.cgColor
            shapeLayerBottom.lineWidth = strokeWidth
            layer.addSublayer(shapeLayerBottom)
    
            let shapeLayerMid = CAShapeLayer()
            let circlePath = UIBezierPath(arcCenter: midPoint , radius: thirdWidth, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
            shapeLayerMid.path = circlePath.cgPath
            shapeLayerMid.strokeColor = grayColor.cgColor
            shapeLayerMid.fillColor = UIColor.clear.cgColor
            shapeLayerMid.lineWidth = strokeWidth
            layer.addSublayer(shapeLayerMid)
        }
    
        fileprivate func drawEnd(_ halfWidth: CGFloat, _ thirdWidth: CGFloat, _ halfHeight: CGFloat, _ midPoint: CGPoint,_ strokeWidth: CGFloat) {
            layer.sublayers?.forEach{ layer in
                layer.removeFromSuperlayer()
            }
            let linePath = UIBezierPath()
            linePath.move(to: CGPoint(x: halfWidth, y: -thirdWidth))
            linePath.addLine(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth))
            linePath.close()
    
            let shapeLayerLine = CAShapeLayer()
            shapeLayerLine.fillColor = UIColor.clear.cgColor
            shapeLayerLine.strokeColor = grayColor.cgColor
            shapeLayerLine.lineWidth = strokeWidth
            shapeLayerLine.path = linePath.cgPath
            layer.addSublayer(shapeLayerLine)
    
            let shapeLayerMid = CAShapeLayer()
            let circlePath = UIBezierPath(arcCenter: midPoint , radius: thirdWidth, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
            shapeLayerMid.path = circlePath.cgPath
            shapeLayerMid.strokeColor = grayColor.cgColor
            shapeLayerMid.fillColor = UIColor.clear.cgColor
            shapeLayerMid.lineWidth = strokeWidth
            layer.addSublayer(shapeLayerMid)
        }
    
        fileprivate func drawNode(_ halfWidth: CGFloat, _ thirdWidth: CGFloat, _ halfHeight: CGFloat, _ midPoint: CGPoint,_ strokeWidth: CGFloat) {
            layer.sublayers?.forEach{ layer in
                layer.removeFromSuperlayer()
            }
            let linePath = UIBezierPath()
            linePath.move(to: CGPoint(x: halfWidth, y: -thirdWidth))
            linePath.addLine(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth))
    
            linePath.move(to: CGPoint(x: halfWidth, y: halfHeight + thirdWidth))
            linePath.addLine(to: CGPoint(x: halfWidth, y: bounds.height))
            linePath.close()
    
            let shapeLayerLine = CAShapeLayer()
            shapeLayerLine.fillColor = UIColor.clear.cgColor
            shapeLayerLine.strokeColor = grayColor.cgColor
            shapeLayerLine.lineWidth = strokeWidth
            shapeLayerLine.path = linePath.cgPath
            layer.addSublayer(shapeLayerLine)
    
            let shapeLayerMid = CAShapeLayer()
            let circlePath = UIBezierPath(arcCenter: midPoint , radius: thirdWidth, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
            shapeLayerMid.path = circlePath.cgPath
            shapeLayerMid.strokeColor = grayColor.cgColor
            shapeLayerMid.fillColor = UIColor.clear.cgColor
            shapeLayerMid.lineWidth = strokeWidth
            layer.addSublayer(shapeLayerMid)
        }
    
        fileprivate func drawOnly(_ width: CGFloat, _ thirdWidth: CGFloat, _ halfWidth: CGFloat, _ halfHeight: CGFloat, _ strokeWidth: CGFloat, _ midPoint: CGPoint) {
            layer.sublayers?.forEach{ layer in
                layer.removeFromSuperlayer()
            }
            let linePathTop = UIBezierPath()
            linePathTop.move(to: CGPoint(x: -width, y: -thirdWidth))
            linePathTop.addCurve(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth), controlPoint1: CGPoint(x: halfWidth, y: -thirdWidth ), controlPoint2: CGPoint(x: halfWidth, y: -thirdWidth))
            linePathTop.move(to: CGPoint(x: 2 * width, y: -thirdWidth))
            linePathTop.addCurve(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth), controlPoint1: CGPoint(x: halfWidth, y: -thirdWidth ), controlPoint2: CGPoint(x: halfWidth, y: -thirdWidth))
            linePathTop.move(to: CGPoint(x: 0, y: -thirdWidth))
            linePathTop.addLine(to: CGPoint(x: width, y: -thirdWidth))
            linePathTop.move(to: CGPoint(x: halfWidth, y: -thirdWidth))
            linePathTop.addLine(to: CGPoint(x: halfWidth, y: halfHeight - thirdWidth))
            linePathTop.close()
    
            let shapeLayerTop = CAShapeLayer()
            shapeLayerTop.path = linePathTop.cgPath
            shapeLayerTop.fillColor = UIColor.clear.cgColor
            shapeLayerTop.strokeColor = purpleColor.cgColor
            shapeLayerTop.lineWidth = strokeWidth
            layer.addSublayer(shapeLayerTop)
    
            let shapeLayerMid = CAShapeLayer()
            let circlePath = UIBezierPath(arcCenter: midPoint , radius: thirdWidth, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
            shapeLayerMid.path = circlePath.cgPath
            shapeLayerMid.strokeColor = grayColor.cgColor
            shapeLayerMid.fillColor = UIColor.clear.cgColor
            shapeLayerMid.lineWidth = strokeWidth
            layer.addSublayer(shapeLayerMid)
        }
    }
    

    这是我能想到的第一个解决方案。可能需要一些调整,但这对我有用。我需要将 UI 的 3 个阶段放置在 UITableViewCell 中。 一个用于第一个单元格,一个用于最后一个单元格,另一个用于其余单元格。

    结果是这样的

    【讨论】:

      【解决方案3】:

      如果不是自定义视图,您可以使用 CAShape 层和 UIBezierPath 来执行此操作 // 如果其自定义视图更容易,只需在 draw 中创建路径并设置颜色下面是您需要创建路径和其他属性的一些方法设置颜色等。如果不是自定义视图,您可以使用带有路径的 CAShapelayer 来实现相同的效果。

      //create your path 
      
      let xpos: CGFloat = yourXpos // do your calculation and set the x and y
      let ypos:cfFloat =  yourYPos
      let path = UIBezierPath() // UIBezierPath is like a pan you draw line arch circle, like pen you can move from one position to another, if you want to close(connected starting and end point) you just call close()
      
      path.move(to: CGPoint(x: xpos, y: yPos)) //move is like the 
      path.addLine(to: CGPoint(x: xpos , y: yPos + 25))
      path.addArc(withCenter: CGPoint(x: xpos + 1, y: yPos + 25), radius: 2, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
      path.close()
      
      path.move()//move to some new place 
      You have to calculate x,y like you already done for Android and just set the correct x and y values
      
      //create shape layer and set the path you just created to the shapelayer and shapelayer to your view
      let shapeLayer = CAShapeLayer() 
      shapeLayer.path = path.cgPath
      self.yourView.layer.addSublayer(shapeLayer)
      shapeLayer.lineWidth = 0.5 // setting the stoke width// thinkness of drawing 
      
      you can set the stroke colour or can fill it
      shapeLayer.fillColor = UIColor.black.cgColor
      shapeLayer.strokeColor = UIColor.green.cgColor
      
      
      PS: I Haven't done the actual calculation but you have already done, so maybe with above help you can easly replicate for iOS.
      

      【讨论】:

      • 谢谢!已经朝着同一个方向努力。这会有所帮助。一个小查询是否可以为我们添加到图层的每个形状设置不同的strokeWidth
      • 我不确定在这种情况下您是否必须创建不同的路径并设置 lineWidth,如果可能,请告诉我。
      • 谢谢!伊朗,我解决了这个问题,它可能效率不高,但它有效。
      猜你喜欢
      • 1970-01-01
      • 2014-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多