Skip to content

Instantly share code, notes, and snippets.

@Mukeshawal
Created January 28, 2020 08:31
Show Gist options
  • Save Mukeshawal/b02dcd34e43302a9e5d86b547afe08e8 to your computer and use it in GitHub Desktop.
Save Mukeshawal/b02dcd34e43302a9e5d86b547afe08e8 to your computer and use it in GitHub Desktop.
//MARK: - common init
private func commonInit(){
shapeLayer.fillColor = setFillColor.cgColor
shapeLayer.actions = ["path" : NSNull(), "position" : NSNull(), "bounds" : NSNull()]
layer.addSublayer(shapeLayer)
shapeLayer.masksToBounds = true
circleLayer.lineWidth = 4
circleLayer.strokeColor = setRefreshCircleColor.cgColor
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.actions = ["path" : NSNull(), "position" : NSNull(), "bounds" : NSNull()]
layer.addSublayer(circleLayer)
}
//MARK: - draw rect
override func draw(_ rect: CGRect) {
guard let _ = containerScrollView else {return}
calculate(rect)
leftTop = CGPoint(x: rect.minX, y: rect.minY)
rightTop = CGPoint(x: rect.maxX, y: rect.minY)
leftBottom = CGPoint(x: rect.minX, y: edgeBottomPointYOffset)
rightBottom = CGPoint(x: rect.maxX, y: edgeBottomPointYOffset)
midBottom = CGPoint(x: xPositionOfPan, y: middleBottomPointYOffset)
///border path of refresh control
let path = CGMutablePath()
path.move(to: leftTop)
path.addLine(to: leftBottom)
path.addLine(to: midBottom)
path.addLine(to: rightBottom)
path.addLine(to: rightTop)
path.closeSubpath()
shapeLayer.path = path
if !refreshingStatus {
///circle refresh path
let draggedFractionCompleted = edgeBottomPointYOffset / thresholdDrag
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0), radius: refreshCircleSize.rawValue, startAngle: getStartAngle(draggedFractionCompleted), endAngle: getStartAngle(draggedFractionCompleted + 0.85), clockwise: true)
circleLayer.path = circlePath.cgPath
}
}
//MARK: - calculation
/// calculates all required dynamic variables and sets frame of layers
/// - Parameter rect: CGRect of view's frame
private func calculate(_ rect : CGRect){
//guard "scrollViewContentYOffset" to be greater than zero
//i.e. user is dragging scroll view downward such that actual content offset of scroll view is negative
guard scrollViewContentYOffset >= 0 else {
middleBottomPointYOffset = 0
edgeBottomPointYOffset = 0
return
}
//calculating y offsets of points
//if refreshing status is false then we have to draw V shape at bottom
if !refreshingStatus{
middleBottomPointYOffset = min(scrollViewContentYOffset, maxHeightOfRefreshControl)
edgeBottomPointYOffset = max((middleBottomPointYOffset - 20),0)
}else{
//else if refreshing status is true then --- straight line at bottom
middleBottomPointYOffset = min(scrollViewContentYOffset, thresholdDrag)
edgeBottomPointYOffset = middleBottomPointYOffset
//then set scroll view's content inset
containerScrollView?.contentInset.top = middleBottomPointYOffset
}
//calculating frame of layer
shapeLayer.frame = CGRect(x: 0, y: 0, width: rect.width, height: middleBottomPointYOffset)
//calculate center of circle
centerForCircle = CGPoint(x: rect.midX, y: edgeBottomPointYOffset - (thresholdDrag / 2))
circleLayer.frame = CGRect(x: centerForCircle.x, y: centerForCircle.y, width: 0, height: 0)
//wondering why are we providing frame with height and width zero and origin to center of circle??
//since animating a layer about z axis by default rotates whole frame of that layer about its origin
//so setting origin of frame of circle layer to center of circle with height and width as zero(basically a pin point \ (•◡•) /)
//then applying rotation about z-axis will rotate our circle in desired way
// kinda hack you want to use if you are ever stuck in these rotation stuff ¯\_(ツ)_/¯
}
/// calculating starting angle to draw circle according to fraction of drag completed
/// - Parameter fractionCompleted: fraction of drag completed
private func getStartAngle(_ fractionCompleted : CGFloat) -> CGFloat{
return ((2 * CGFloat.pi) * (fractionCompleted))
}
//MARK: - animation
/// animates refresh circle
private func animateRefreshCircle(){
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0.0
animation.toValue = CGFloat.pi * CGFloat(2.0)
animation.duration = 1.5
animation.repeatCount = .infinity
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
circleLayer.add(animation, forKey: "rotate")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment