I am facing a weird issue. I don't know if I am making some mistake or it is a fault with UIBezierPath.
I want to have corner radius of UIBezierPath around 40 percent of width or height, but the issue is as soon as I increase 0.1% corner radius it becomes circular. for eg. it displays rounded corner for 0.327% but as soon as I make it 0.328% it becomes circular.
Please let me know what I can do to solve my problem. Current scenario with corner percentage
Class for my view is below.
class MyView: UIView {
var fraction : CGFloat = 0.5 {
didSet {
progressBorder.strokeEnd = fraction
}
}
var strokeWidth : CGFloat = 4.0 {
didSet {
}
}
@IBInspectable var cornerPercentage : CGFloat = 0.327
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layer.cornerRadius = frame.height * cornerPercentage
// backgroundColor = .red
}
let progressBorder = CAShapeLayer()
override func draw(_ rect: CGRect) {
let cornerrad : CGFloat = (frame.height) * cornerPercentage
layer.borderColor = UIColor.systemPink.cgColor
layer.cornerRadius = cornerrad
let border = CAShapeLayer()
// make sure this path coincides with the border of the view
border.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerrad).cgPath
border.cornerRadius = cornerrad
border.strokeStart = 0
border.strokeEnd = 1
border.strokeColor = AppColor.fieldsBackground().cgColor
border.lineWidth = strokeWidth
border.fillColor = nil
// make sure this path coincides with the border of the view
progressBorder.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerrad).cgPath
progressBorder.cornerRadius = cornerrad
progressBorder.lineJoin = .miter
progressBorder.lineCap = .round
progressBorder.strokeStart = 0
progressBorder.strokeEnd = fraction
progressBorder.strokeColor = AppColor.buttonsPrimary().cgColor
progressBorder.lineWidth = strokeWidth
progressBorder.fillColor = nil
layer.addSublayer(border)
layer.addSublayer(progressBorder)
backgroundColor = .white
}
}
I want a rounded progress view with 40% of square view's height or width
CodePudding user response:
If you follow the link in my comment, you'll see discussion of the "bug" in UIBezierPath(roundedRect: ...)
.
Here's a modified version of your MyView
- with cornerPercentage = 0.4
- that avoids that bug:
class MyView: UIView {
var fraction : CGFloat = 0.5 {
didSet {
progressBorder.strokeEnd = fraction
}
}
var strokeWidth : CGFloat = 4.0 {
didSet {
border.lineWidth = strokeWidth
progressBorder.lineWidth = strokeWidth
}
}
@IBInspectable var cornerPercentage : CGFloat = 0.4 // 0.327
let border = CAShapeLayer()
let progressBorder = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layer.addSublayer(border)
layer.addSublayer(progressBorder)
border.lineWidth = strokeWidth
progressBorder.lineWidth = strokeWidth
border.fillColor = nil
progressBorder.fillColor = nil
layer.borderColor = UIColor.systemPink.cgColor
}
override func layoutSubviews() {
super.layoutSubviews()
let cornerrad : CGFloat = (bounds.height) * cornerPercentage
layer.cornerRadius = cornerrad
let r: CGRect = bounds
// center points for the corner arcs
let ptCTR: CGPoint = CGPoint(x: r.maxX - cornerrad, y: r.minY cornerrad)
let ptCBR: CGPoint = CGPoint(x: r.maxX - cornerrad, y: r.maxY - cornerrad)
let ptCBL: CGPoint = CGPoint(x: r.minX cornerrad, y: r.maxY - cornerrad)
let ptCTL: CGPoint = CGPoint(x: r.minX cornerrad, y: r.minY cornerrad)
let bez: UIBezierPath = UIBezierPath()
// Top-Right corner
bez.addArc(withCenter: ptCTR, radius: cornerrad, startAngle: .pi * 1.5, endAngle: .pi * 0.0, clockwise: true)
// Bottom-Right corner
bez.addArc(withCenter: ptCBR, radius: cornerrad, startAngle: .pi * 0.0, endAngle: .pi * 0.5, clockwise: true)
// Bottom-Left corner
bez.addArc(withCenter: ptCBL, radius: cornerrad, startAngle: .pi * 0.5, endAngle: .pi * 1.0, clockwise: true)
// Top-Left corner
bez.addArc(withCenter: ptCTL, radius: cornerrad, startAngle: .pi * 1.0, endAngle: .pi * 1.5, clockwise: true)
// close the path
bez.close()
// make sure this path coincides with the border of the view
border.path = bez.cgPath
border.strokeStart = 0
border.strokeEnd = 1
border.strokeColor = UIColor.lightGray.cgColor // AppColor.fieldsBackground().cgColor
// make sure this path coincides with the border of the view
progressBorder.path = bez.cgPath
progressBorder.strokeStart = 0
progressBorder.strokeEnd = fraction
progressBorder.strokeColor = UIColor.systemBlue.cgColor // AppColor.buttonsPrimary().cgColor
}
}
and an example view controller -- fraction
starts at 0.1
and increases by 0.1
with each tap:
class ViewController: UIViewController {
let myTestView = MyView()
override func viewDidLoad() {
super.viewDidLoad()
myTestView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myTestView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
myTestView.centerXAnchor.constraint(equalTo: g.centerXAnchor, constant: 0.0),
myTestView.centerYAnchor.constraint(equalTo: g.centerYAnchor, constant: 0.0),
myTestView.widthAnchor.constraint(equalToConstant: 300.0),
myTestView.heightAnchor.constraint(equalToConstant: 50.0),
])
myTestView.fraction = 0.1
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
myTestView.fraction = 0.1
}
}
As a side note: you'll see I moved your code around a bit. You can add the shape layers and set their non-changing properties during init, then set the path(s) and other changing properties in layoutSubviews()
. It's not a good idea to be adding sublayers inside draw()
.
CodePudding user response:
The above answer worked with corner radius. But now I want the progress to be start from exact center. Currently It's starting from a point away from center.
The progress strokeEnd in image is 0.5.
I want something like this