Ios 如何使用UIBezierPath绘制此复选框图标?

Ios 如何使用UIBezierPath绘制此复选框图标?,ios,swift,math,geometry,uibezierpath,Ios,Swift,Math,Geometry,Uibezierpath,我正在为一个应用程序创建一些自定义按钮,有几个图标,我已经在理论上设计了它们,但是我很难用iOS的UIBezierPath语言绘制这个讨厌的“复选框”图标 作为一般规则,图标的边界为5 x 5,图标内包含的图形的边界为3 x 3。(是的,我知道复选框超出了3 x 3的边界) 这是一个复选框图标和一个“加号”图标(是的,我用Corel DRAW 12打球,所以…): 你看,“加号”图标,我可以这样做: class BezierPathFactory { // "gridSpacing

我正在为一个应用程序创建一些自定义按钮,有几个图标,我已经在理论上设计了它们,但是我很难用iOS的UIBezierPath语言绘制这个讨厌的“复选框”图标

作为一般规则,图标的边界为5 x 5,图标内包含的图形的边界为3 x 3。(是的,我知道复选框超出了3 x 3的边界)

这是一个复选框图标和一个“加号”图标(是的,我用Corel DRAW 12打球,所以…):

你看,“加号”图标,我可以这样做:

class BezierPathFactory {


    // "gridSpacing" is expected to be the containing view's width / 5
    static func plus(gridSpacing G: CGFloat) -> UIBezierPath {

        let path = UIBezierPath()

        let startPoint = CGPoint(x:2*G,y:G)

        path.move(to: startPoint)
        path.addLine(to: CGPoint(x:2*G,y:2*G))
        path.addLine(to: CGPoint(x:G,y:2*G))
        path.addLine(to: CGPoint(x:G,y:3*G))
        path.addLine(to: CGPoint(x:2*G,y:3*G))
        path.addLine(to: CGPoint(x:2*G,y:4*G))
        path.addLine(to: CGPoint(x:3*G,y:4*G))
        path.addLine(to: CGPoint(x:3*G,y:3*G))
        path.addLine(to: CGPoint(x:4*G,y:3*G))
        path.addLine(to: CGPoint(x:4*G,y:2*G))
        path.addLine(to: CGPoint(x:3*G,y:2*G))
        path.addLine(to: CGPoint(x:3*G,y:G))
        path.addLine(to: startPoint)

        path.close()

        return path

}

// ...

}
但是,复选框图标让我的头很痛

到目前为止,我已查明以下情况:

  • 复选标记的长宽比(顺时针旋转45度)为4:2

  • 复选标记右上角的CG点与“内部(3 x 3)框”的右上角相同

  • 如果图标的网格间距为1,则从45度的角度来看,复选标记的每个“块”都是(sqrt(18)/5)高和/或宽

  • 家里有数学家吗

    我仍在努力,但可以尝试一下

    这就是我迄今为止所做的:

    static func checkMark(gridSpacing G: CGFloat) -> UIBezierPath {
    
    
            let blurp = UIBezierPath()
    
            let CM_hyp = sqrt((18*G)/5)
    
            let CM_opp_or_adj = sqrt( ((CM_hyp)*(CM_hyp)) / 2 )
    
            let startPoint = CGPoint(x: 4*G, y: G)
    
            blurp.move(to: startPoint)
            blurp.addLine(to: CGPoint(x: (4*G)+CM_opp_or_adj, y: G + CM_opp_or_adj))
            blurp.addLine(to: CGPoint(x: 4*G-(3*CM_opp_or_adj), y: 4*G) )
            blurp.addLine(to: CGPoint( x: G, y:(4*G) - 2*CM_opp_or_adj ))
            blurp.addLine(to: CGPoint( x: G + CM_opp_or_adj, y: (2*G) + CM_opp_or_adj) )
            //6
            blurp.addLine(to: CGPoint( x: 2*G + CM_opp_or_adj, y:      (4*G) - 2*CM_opp_or_adj   )     )
            blurp.addLine(to: startPoint)
    
    
            blurp.close()
    
    
            return blurp
    
        }
    

    但它看起来很傻。我做错了。我做了一个自定义复选框作为按钮

    @IBDesignable class SquareCheckBox: UIButton {
    //MARK: - Properties
    @IBInspectable var isOn:Bool = false{
        didSet{
            self.setNeedsDisplay()
        }
    }
    //gives the actual functionality of the button
    
    
    @IBInspectable var tickWidth: CGFloat = 2.0
    //decides the width of the tick
    /*
     THIS VALUE CANNOT EXCEED THE HALF OF THE BUTTON'S HEIGHT OR GO BELOW ZERO. IT WILL RESET TO 3.0 IN ANY SUCH CASE.
     */
    
    
    @IBInspectable var borderWidth: CGFloat = 3.0
    //decides the width of the border of the button
    /*
     THIS VALUE WILL BE RESET TO 3.0 IF THE DEVELOPER EXCEEDS THE 1/4TH OF THE BUTTON'S HEIGHT OR BELOW ZERO.
     */
    
    
    @IBInspectable var borderRadius: CGFloat = 3.0
    //decides the corner radius of the button
    /*
     THIS VALUE CANNOT EXCEED THE HALF OF THE BUTTON'S HEIGHT OR GO BELOW ZERO. IT WILL RESET TO 3.0 IN ANY SUCH CASE.
     */
    
    
    @IBInspectable public var borderColor: UIColor = UIColor(red: 255/255, green: 0, blue: 0, alpha: 1)
    //decides the color of the border of the button
    
    
    @IBInspectable public var BGColorOn: UIColor = UIColor(red: 255/255, green: 0, blue: 0, alpha: 1)
    //decides the color of button's background when it is checked
    
    
    @IBInspectable public var BGColorOff: UIColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
    //decides the color of button's background when it is checked
    
    
    @IBInspectable public var tickColor: UIColor = UIColor.white
    //decides the color of the tick
    
    //MARK: - Overriden Functions
    override init(frame: CGRect) {
        super.init(frame: frame)
    
        self.setTitle(nil, for: .normal)
        //removing any title as it doesn't allow the layers in button to form and crashes the App
    }
    // Xcode uses this to render the button in the storyboard.
    
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    // The storyboard loader uses this at runtime.
    
    
    override func draw(_ rect: CGRect) {
        let boxDim = min(bounds.height, bounds.width)
        self.clipsToBounds = true
        self.layer.borderColor = self.borderColor.cgColor
    
    
        //NOTE: we cannot set the value for the radius more than half of the width if the width is smaller than height OR we cannot set the value for the radius more than half of the width if the height is smaller than the width
        if borderRadius < 0 || borderRadius > boxDim/2 {
            self.layer.cornerRadius = 3.0
        } else {
            self.layer.cornerRadius = self.borderRadius
        }
    
        //creating box
        let path = UIBezierPath(roundedRect: rect, cornerRadius: self.borderRadius)
        //creating tick
        let tickPath = UIBezierPath()
    
        tickPath.lineWidth = 2.0
    
        //tick's path
        if bounds.width > bounds.height{
            //when width is greater than height
            tickPath.move(to: CGPoint(x: bounds.width/2 - boxDim/3, y: boxDim/2))
            tickPath.addLine(to: CGPoint(x: bounds.width/2 - boxDim/6, y: ((boxDim)*3)/4))
            tickPath.addLine(to: CGPoint(x: bounds.width/2 + boxDim/3, y: boxDim/4))
        } else if bounds.width < bounds.height{
            //when height is greater than width
            tickPath.move(to: CGPoint(x: boxDim/6, y: bounds.height/2))
            tickPath.addLine(to: CGPoint(x: ((boxDim)*2)/6, y: (((boxDim)*3)/4) - boxDim/2 + bounds.height/2))
            tickPath.addLine(to: CGPoint(x: ((boxDim)*5)/6, y: bounds.height/2 - (boxDim/4)))
        } else {
            //when it's a square
            tickPath.move(to: CGPoint(x: boxDim/6, y: boxDim/2))
            tickPath.addLine(to: CGPoint(x: ((boxDim)*2)/6, y: ((boxDim)*3)/4))
            tickPath.addLine(to: CGPoint(x: ((boxDim)*5)/6, y: boxDim/4))
        }
    
    
    
        if isOn{
            self.layer.borderWidth = 0.0
            BGColorOn.setFill()//setting background color for when box is on
            path.fill()
    
            //creating sublayer
            let pathLayer = CAShapeLayer()
            pathLayer.frame = self.bounds
            pathLayer.path = tickPath.cgPath
            pathLayer.strokeColor = tickColor.cgColor//setting tick color
            pathLayer.fillColor = nil
            //NOTE: we cannot set the value for the width of tick more than one-fourth of width if width is smaller than height OR we cannot set the value for the width of tick more than one-fourth of width if height is smaller than width
            //we cannot set the value for the width of tick less than one
            if tickWidth < 1 || tickWidth > boxDim/4 {
                pathLayer.lineWidth = 2
            }else {
                pathLayer.lineWidth = tickWidth
            }
    
            pathLayer.lineJoin = kCALineJoinBevel
    
            //adding sublayer
            self.layer.addSublayer(pathLayer)
    
            //animating
            let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
            pathAnimation.duration = 0.5
            pathAnimation.fromValue = 0.0
            pathAnimation.toValue = 1.0
            pathLayer.add(pathAnimation, forKey: "strokeEnd")
        } else {
            if borderWidth < 0 || borderWidth > boxDim/4 {
                self.layer.borderWidth = 3.0
            } else {
                self.layer.borderWidth = self.borderWidth
            }
            BGColorOff.setFill()
            path.fill()
            self.layer.sublayers?.removeAll()
            //removing all sublayers
        }
    }
    }
    
    @IBDesignable类SquareCheckBox:UIButton{
    //标记:-属性
    @IBInspectable变量:Bool=false{
    迪塞特{
    self.setNeedsDisplay()
    }
    }
    //给出按钮的实际功能
    @IBInspectable var tickWidth:CGFloat=2.0
    //决定勾号的宽度
    /*
    此值不能超过按钮高度的一半或低于零。在任何情况下,它将重置为3.0。
    */
    @IBInspectable var borderWidth:CGFloat=3.0
    //决定按钮边框的宽度
    /*
    如果显影剂超过按钮高度的1/4或低于零,此值将重置为3.0。
    */
    @IBInspectable var borderRadius:CGFloat=3.0
    //决定按钮的角半径
    /*
    此值不能超过按钮高度的一半或低于零。在任何情况下,它将重置为3.0。
    */
    @IBInspectable public var borderColor:UIColor=UIColor(红色:255/255,绿色:0,蓝色:0,alpha:1)
    //决定按钮边框的颜色
    @IBInspectable public var BGColorOn:UIColor=UIColor(红色:255/255,绿色:0,蓝色:0,alpha:1)
    //确定选中按钮时按钮背景的颜色
    @IBInspectable public var BGColorOff:UIColor=UIColor(红色:1,绿色:1,蓝色:1,alpha:1)
    //确定选中按钮时按钮背景的颜色
    @i可检测的公共颜色:UIColor=UIColor.white
    //决定勾号的颜色
    //标记:-重写的函数
    重写初始化(帧:CGRect){
    super.init(frame:frame)
    self.setTitle(无,表示:。正常)
    //删除任何标题,因为它不允许按钮中的层形成并使应用程序崩溃
    }
    //Xcode使用此选项渲染情节提要中的按钮。
    必需的初始化?(编码器aDecoder:NSCoder){
    super.init(编码者:aDecoder)
    }
    //情节提要加载程序在运行时使用此选项。
    重写函数绘图(rect:CGRect){
    设boxDim=min(bounds.height,bounds.width)
    self.clipstobunds=true
    self.layer.borderColor=self.borderColor.cgColor
    //注意:如果宽度小于高度,我们无法设置半径大于宽度一半的值;如果高度小于宽度,我们无法设置半径大于宽度一半的值
    如果borderRadius<0 | | borderRadius>boxDim/2{
    self.layer.cornerRadius=3.0
    }否则{
    self.layer.cornerRadius=self.borderRadius
    }
    //创建框
    let path=UIBezierPath(roundedRect:rect,cornerRadius:self.borderRadius)
    //创建勾号
    让tickPath=UIBezierPath()
    tickPath.lineWidth=2.0
    //滴答声
    如果bounds.width>bounds.height{
    //当宽度大于高度时
    tickPath.move(到:CGPoint(x:bounds.width/2-boxDim/3,y:boxDim/2))
    tickPath.addLine(to:CGPoint(x:bounds.width/2-boxDim/6,y:((boxDim)*3)/4))
    tickPath.addLine(to:CGPoint(x:bounds.width/2+boxDim/3,y:boxDim/4))
    }如果bounds.widthboxDim/4{
    pathLayer.lineWidth=2
    }否则{
    pathLayer.lineWidth=tickWidth
    }
    pathLayer.lineJoin=kcalinejoin
    //添加子层
    self.layer.addSublayer(路径层)
    //动画
    让pathAnimation=Cabasicanization(关键路径:“strokeEnd”)
    pathAnimation.duration=0.5
    帕坦尼玛
    
    static func checkMark(gridSpacing G: CGFloat) -> UIBezierPath {        
    
            let blurp = UIBezierPath()
    
            let CM_hyp = ((sqrt(18))/5) * G
    
            let CM_opp_or_adj = sqrt( ((CM_hyp)*(CM_hyp)) / 2 )
    
            let startPoint = CGPoint(x: 4*G, y: G)
    
            blurp.move(to: startPoint)
            blurp.addLine(to: CGPoint(x: (4*G)+CM_opp_or_adj, y: G + CM_opp_or_adj))
            blurp.addLine(to: CGPoint(x: (4*G)-(3*CM_opp_or_adj), y: 4*G) )
            //3
            blurp.addLine(to: CGPoint( x: G, y:(4*G) - 2*CM_opp_or_adj ))
    
            blurp.addLine(to: CGPoint( x: G + CM_opp_or_adj, y: (4*G) - 3*CM_opp_or_adj ) )
            blurp.addLine(to: CGPoint( x: (4*G)-(3*CM_opp_or_adj), y: (4*G) - 2*CM_opp_or_adj ) )
            blurp.addLine(to: startPoint)
    
            blurp.close()
            // !!
            return blurp
    
        }
    
    static func checkMarkBox(gridSpacing G: CGFloat) -> UIBezierPath {
    
            let boxPath = UIBezierPath()
    
            boxPath.move(to: CGPoint(x:G, y: G))
            boxPath.addLine(to: CGPoint(x:4*G, y: G))
            boxPath.addLine(to: CGPoint(x:4*G, y: 4*G))
            boxPath.addLine(to: CGPoint(x:G, y: 4*G))
            boxPath.addLine(to: CGPoint(x:G, y: G))
    
            boxPath.close()
    
            return boxPath
    
        }