iSwift iSwift - 25 days ago 6
iOS Question

Create a Timeline View using BezierPath in Tableview

i am trying to draw a milestone in TableView with 5 rows, i have added a label in cell as "O",here row height is 44 and am trying to connect these dots
Something like this
O------O------O------O-----O vertically, but i am unable to figure out what am missing here, refer the following code

class StopslistTableViewCell:UITableViewCell{

@IBOutlet weak var milestoneLbl: UILabel!

override func draw(_ rect: CGRect) {

let scrrenX = UIScreen.main.bounds.width - 30

let frametosuperview = milestoneLbl.convert(milestoneLbl!.frame, to: self.superview)

print("dotframetosuperview",frametosuperview)

//print(CGPoint(x: scrrenX, y: previousposY + rect.size.height))

//frametosuperview.origin.y > 40 ? frametosuperview.origin.y - 20 : frametosuperview.origin.y

let path = UIBezierPath()
path.move(to: CGPoint(x: scrrenX, y:frametosuperview.origin.y < 30 ? frametosuperview.origin.y : frametosuperview.origin.y - frametosuperview.height))

print("move to point--->",frametosuperview.origin.y < 30 ? frametosuperview.origin.y : frametosuperview.origin.y - frametosuperview.height)

path.addLine(to: CGPoint(x: scrrenX, y: frametosuperview.origin.y < 30 ? frametosuperview.height + 24 : (frametosuperview.origin.y + 24)))
print("add line to point--->",CGPoint(x: scrrenX, y: frametosuperview.origin.y < 30 ? frametosuperview.height + 24 : (frametosuperview.origin.y + 24)))

path.lineWidth = 5

UIColor.red.setStroke()
path.stroke()

setNeedsDisplay()
}


debug output

dotframetosuperview (560.0, 20.0, 20.0, 20.0)
move to point---> 20.0
add line to point---> (290.0, 44.0)
dotframetosuperview (560.0, 64.0, 20.0, 20.0)
move to point---> 44.0
add line to point---> (290.0, 88.0)
dotframetosuperview (560.0, 108.0, 20.0, 20.0)
move to point---> 88.0
add line to point---> (290.0, 132.0)
dotframetosuperview (560.0, 152.0, 20.0, 20.0)
move to point---> 132.0
add line to point---> (290.0, 176.0)
dotframetosuperview (560.0, 196.0, 20.0, 20.0)
move to point---> 176.0
add line to point---> (290.0, 220.0)


i need something like this

enter image description here

I am unable to get the desired results,am i missing something here..?

Answer

You dont need to create a UILabel.Instead create a circle and a Line with Dashed pattern as :

let offSet:CGFloat = 20.0
let circleRadius:CGFloat = 5.0

override func draw(_ rect: CGRect) {

    //creating a circle
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: offSet,y: circleRadius), radius: CGFloat(circleRadius), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = circlePath.cgPath
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.black.cgColor
    shapeLayer.lineWidth = 2.0
    circlePath.stroke()

    //creating a line with dashed pattern
    let  dashPath = UIBezierPath()
    let  startPoint = CGPoint(x:offSet  ,y:circleRadius * 2)
    dashPath.move(to: startPoint)

    let  endPoint = CGPoint(x:offSet,y:
        self.bounds.maxY)
    dashPath.addLine(to: endPoint)

    let  dashes: [ CGFloat ] = [4, 1] //line with dash pattern of 4 thick and i unit space
    dashPath.setLineDash(dashes, count: dashes.count, phase: 0)
    dashPath.lineWidth = 2.0
    dashPath.lineCapStyle = .butt
    UIColor.black.set()
    dashPath.stroke()

}

Output: enter image description here

EDIT: Since OP wants the timeline circle to appear from the center of the cell.

var allRows = Int()
var currentIndexPath = IndexPath()
let offSet:CGFloat = 20.0
let circleRadius:CGFloat = 5.0


override func draw(_ rect: CGRect) {

    //creating a circle
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: offSet,y: self.bounds.midY), radius: CGFloat(circleRadius), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = circlePath.cgPath
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.black.cgColor
    shapeLayer.lineWidth = 2.0
    circlePath.stroke()

    //creating a line with dashed pattern
    let  dashPath = UIBezierPath()
    var startPoint = CGPoint()

    if currentIndexPath.row  == 0{

        startPoint = CGPoint(x:offSet  ,y:self.bounds.midY)
    }else{

        startPoint = CGPoint(x:offSet  ,y:0)
    }
    dashPath.move(to: startPoint)

    var endPoint = CGPoint()

    if currentIndexPath.row == allRows-1{

        endPoint = CGPoint(x:offSet,y:
            self.bounds.midY)
    }else{

        endPoint = CGPoint(x:offSet,y:
            self.bounds.maxY)

    }
    dashPath.addLine(to: endPoint)

    let  dashes: [ CGFloat ] = [4, 1] //line with dash pattern of 4 thick and i unit space
    dashPath.setLineDash(dashes, count: dashes.count, phase: 0)
    dashPath.lineWidth = 2.0
    dashPath.lineCapStyle = .butt
    UIColor.black.set()
    dashPath.stroke()
}

And your cellForRowAt indexPath should be configure as

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        .......
        cell.allRows = totalNumberOfRows
        cell.currentIndexPath = indexPath
        return cell


    }

This results in following O/P: enter image description here