Search code examples
swift3uibezierpathuigraphicscontext

Creating Upside Arrow


I am trying to show arrow of my selected page menu. But, the arrow which I draw didn't show as I expected. It needs to be up

Here is my code:

class ArrowView : UIView {

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override init(frame: CGRect) {
    super.init(frame: frame)
    self.backgroundColor = UIColor.clear
}

var rect: CGRect!
var arrorBackgroundColor: UIColor?

var midX: CGFloat { return rect.midX }
var midY: CGFloat { return rect.midY }
var maxX: CGFloat { return rect.maxX }
var maxY: CGFloat { return rect.maxY }

override func draw(_ rect: CGRect) {
    self.rect = rect

    let ctx = UIGraphicsGetCurrentContext()

    ctx?.beginPath()
    ctx?.move(to: CGPoint(x: 0, y: 0))

//        Old code which the arrow was down
//        ctx?.addQuadCurve(to: CGPoint(x:maxX * 0.2 , y: maxY * 0.2), control: CGPoint(x: maxX * 0.12, y: 0))
//        ctx?.addLine(to: CGPoint(x: midX - maxX * 0.05, y: maxY * 0.9))
//        ctx?.addQuadCurve(to: CGPoint(x: midX + maxX * 0.05, y: maxY * 0.9), control: CGPoint(x: midX, y: maxY))
//        ctx?.addLine(to: CGPoint(x: maxX * 0.8, y: maxY * 0.2))
//        ctx?.addQuadCurve(to: CGPoint(x: maxX, y: 0), control: CGPoint(x: maxX * 0.88, y: 0))

    ctx?.addLine(to: CGPoint(x: midX - maxX * 106.5, y: maxY * 65.5))
    ctx?.addLine(to: CGPoint(x: maxX * 128.5, y: maxY * 88.5))
    ctx?.addLine(to: CGPoint(x: 85.5, y: 88.5))
    ctx?.closePath()

    ctx?.setFillColor((arrorBackgroundColor?.cgColor)!)
    ctx?.fillPath();
}

}

And here how it goes

enter image description here

Actually I am trying to customize this library : https://github.com/azurechen/ACTabScrollView

And trying to archive like this : https://puu.sh/viWwc/35a6bddb0d.png

Since I am beginner with UIBezierPath , any help is appreciated.


Solution

  • I don't understand the constants in your calls to addLine(to:) and I think the implementation of ArrowView is unnecessarily weird.

    You seem to be confused about midX/maxX/... (or I'm just too dense to get it right now). The CGRect documentation should help.

    I think the original author of ArrowView (from the github project you linked to) somehow mixed up bounds and frame when setting the arrow's position. Oh, and he also abused the rect parameter in draw(_:), see the docs. Specifically (emphasis mine):

    rect
    The portion of the view’s bounds that needs to be updated. The first time your view is drawn, this rectangle is typically the entire visible bounds of your view. However, during subsequent drawing operations, the rectangle may specify only part of your view.

    Also, ACTabScrollView seems to work only sometimes. In my shallow experiments, it messed up view positioning most of the time.

    Anyways, to implement a simple UIView for an upwards pointing arrow, this works:

    class ArrowView : UIView {
        var arrorBackgroundColor: UIColor = UIColor.red
    
        override func draw(_ rect: CGRect) {
            guard let ctx = UIGraphicsGetCurrentContext() else { return }
    
            // Sample to really fill the background before drawing the arrow.    
            ctx.setFillColor(UIColor.yellow.cgColor)
            ctx.fill(bounds)
    
            ctx.beginPath()
            ctx.move(to: CGPoint(x: 0, y: bounds.maxY))
            ctx.addLine(to: CGPoint(x: bounds.midX, y: bounds.minY))
            ctx.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
            ctx.addLine(to: CGPoint(x: 0, y: bounds.maxY))
            ctx.closePath()
    
            ctx.setFillColor(arrorBackgroundColor.cgColor)
            ctx.fillPath()
        }
    }
    

    The core is using the view's bounds to compute the coordinates.

    From the UIView docs:

    The geometry of a view is defined by its frame, bounds, and center properties. The frame defines the origin and dimensions of the view in the coordinate system of its superview and is commonly used during layout to adjust the size or position of the view. The center property can be used to adjust the position of the view without changing its size. The bounds defines the internal dimensions of the view as it sees them and is used almost exclusively in custom drawing code. The size portion of the frame and bounds rectangles are coupled together so that changing the size of either rectangle updates the size of both.

    I think arrorBackgroundColor should really be called arrowColor (because it is used as the color of the arrow) and you might have another property arrorBackgroundColor that really is for the background. I added two lines that fill the background with yellow as an example.