Reputation: 4744
I want to drive a smooth curve that curve's 1/3 is outward 2/3 is inward.This is what I want to achieve.
So far , I tried this with adding 1 curve like :
var height: CGFloat = UIScreen.main.bounds.height / 14
let centerWidth = self.frame.width / 2
let screenView = UIScreen.main.bounds
let width = (screenView.width - (2*screenView.width/40)) / 10
path.move(to: CGPoint(x: 0, y: 0)) // start top left
path.addLine(to: CGPoint(x: (centerWidth - width * 2.5), y: 0)) // the beginning of the trough
// first curve down
path.addCurve(to: CGPoint(x: centerWidth , y: height),
controlPoint1: CGPoint(x: (centerWidth - width * 1.33), y: 0), controlPoint2: CGPoint(x: centerWidth - width * 1.33 , y: height))
But when I try this outward and inward curve's have same height. So I changed it and tried adding 2 curve like :
path.move(to: CGPoint(x: 0, y: 0)) // start top left
path.addLine(to: CGPoint(x: (centerWidth - width * 2.5), y: 0)) // the beginning of the trough
// first curve down
path.addCurve(to: CGPoint(x: centerWidth - width * 1.33 , y: height/3),
controlPoint1: CGPoint(x: (centerWidth - width * 1.33), y: 0), controlPoint2: CGPoint(x: centerWidth - width * 1.33 , y: height/3))
path.addCurve(to: CGPoint(x: centerWidth, y: height),
controlPoint1: CGPoint(x: centerWidth - width * 1.33 , y: height/3), controlPoint2: CGPoint(x: centerWidth - width , y: height ))
It similar to what I want but junction point doesnt look the curve is a single piece
I have tried so many things but cant draw what I want exacly.So How can I draw a curve that endpoint height's 1/3 gonna be outward and 2/3 inward that looks like a one piece.
I'm open to all kinds of ideas to draw.Regards
NOTE
I'm trying to customize tabbar
's center button which height value is UIScreen.main.bounds.height
and width is `
let screenView = UIScreen.main.bounds
let width = (screenView.width - (2*screenView.width/40))`
Upvotes: 0
Views: 281
Reputation: 437432
As I look at your designer’s rendering, if you are breaking this into two separate Bézier curves, the key observation is that the top ⅓ curve is symmetrical (with respect to itself) as is the bottom ⅔ curve. And for symmetrical Bézier curves, a quad Bézier is easier to deal with.
And to make the inflection point seamless, you’ll want to ensure that two control points (one for the quad Bézier above the inflection point, the other for the quad Bézier below the inflection point) are collinear with the inflection point itself. E.g.:
It takes a little trigonometry to figure out the placement of these control points, e.g.
let center = CGPoint(x: view.bounds.midX, y: 200)
let radius: CGFloat = 140
let curveOffsetBottom: CGFloat = 30
let curveOffsetInflection: CGFloat = 50
greenCircularShapeLayer.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath
let h = radius + curveOffsetBottom + 50
let curveBottom = CGPoint(x: center.x,
y: center.y + radius + curveOffsetBottom)
let y0 = radius + curveOffsetBottom - h * 2 / 3
let t0 = asin(y0 / (radius + curveOffsetInflection))
let x0 = y0 / tan(t0)
let inflectionPoint = CGPoint(x: center.x - x0, y: center.y + y0)
let t1 = atan((curveBottom.x - inflectionPoint.x) / (curveBottom.y - inflectionPoint.y))
let t2 = 2 * (.pi / 2 - t1)
let x2 = (curveBottom.y - inflectionPoint.y) / tan(t2)
let x1 = x2 / 2
let cp1 = CGPoint(x: inflectionPoint.x - x1, y: curveBottom.y - h)
let cp2 = CGPoint(x: inflectionPoint.x + x2, y: curveBottom.y)
let curveTop = CGPoint(x: cp1.x - cp1.distance(to: inflectionPoint), y: curveBottom.y - h)
let path = UIBezierPath()
path.move(to: curveTop)
path.addQuadCurve(to: inflectionPoint, controlPoint: cp1)
path.addQuadCurve(to: curveBottom, controlPoint: cp2)
path.addLine(to: CGPoint(x: view.bounds.maxX, y: path.currentPoint.y))
path.addLine(to: CGPoint(x: view.bounds.maxX, y: view.bounds.maxY))
path.addLine(to: CGPoint(x: view.bounds.minX, y: view.bounds.maxY))
path.addLine(to: CGPoint(x: view.bounds.minX, y: curveTop.y))
path.close()
blackCurveShapeLayer.path = path.cgPath
Where
extension CGPoint {
func distance(to point: CGPoint) -> CGFloat {
hypot(point.x - x, point.y - y)
}
}
Upvotes: 1