【问题标题】:How to draw this checkbox icon using UIBezierPath?如何使用 UIBezierPath 绘制此复选框图标?
【发布时间】:2018-04-26 03:14:08
【问题描述】:

我正在为一个应用程序创建一些自定义按钮,有几个图标,我在理论上都设计了它们,但我无法弄清楚如何在 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

}

// ...

}

但是,复选框图标让我很头疼。

到目前为止,我已经确定了以下几点:

  1. 对勾的长宽比(顺时针旋转45度)为4:2

  2. 勾号右上角的CGPoint与“inside (3 x 3) box”的右上角相同

  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

    }

但它看起来很傻。我做错了什么。

【问题讨论】:

  • 我认为你可以只添加 2 条 90 度弧线并增加宽度而不是绘制......一切?
  • 否定!!!! :)
  • 我推荐PaintCode
  • 我会画一个向后的 L 并旋转它。这不是我们刚刚在你的另一个问题中学到的吗???
  • PaintCode 是最好的,但如果你想手动完成,我会使用 0,0 -> 1,1 的比例,所以点可以缩放到任何大小,但你可以做简单的数学运算从光栅图形。假设一个点在 100x100 图像中的 43,33,所以你添加一个点 @ (width *.43, height * .33)

标签: ios swift math geometry uibezierpath


【解决方案1】:

我做了一个自定义复选框作为按钮

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

它有各种属性,您可以通过创建此类的实例来调整它们

【讨论】:

  • 感谢演示...我也有一个自定义类,但它有一个可检查的类型:控制图标是什么的 Int 变量...其中有很多...有点像计算器...在 draw() 方法中,它从我的 UIBezierPathFactory 类中的静态方法中获取。你的方式可能更好,但我现在就这样做。还发布了我自己的答案......我需要勾选“只是这样”,因为我非常细致。
【解决方案2】:

如果有人有兴趣,我已经开始工作了......

这基本上是一堆数学错误和百加得惹的祸。

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

    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-16
    • 2011-09-20
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多