Created
January 28, 2020 08:31
-
-
Save Mukeshawal/b02dcd34e43302a9e5d86b547afe08e8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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