I'm trying to draw a bezier path with a quadratic curve in the middle. The curve works well on iPhone 8 and XS, but is not responsive (i.e. not rendered correctly) on other devices.
Below is the image of the curve in iPhone XS (correct)
and iPhone XR (incorrect)
I've tried to use the view's constraint to get the middle value of the line but somehow it still not working
Here is the code where i'm drawing the path:
//self.viewTabBorder is the grey line, which is a uiview with 1 pixel height
override func viewWillAppear(_ animated: Bool) {
let path = UIBezierPath()
path.move(to: CGPoint(x: self.viewTabBorder.center.x - self.btnHome.frame.width + 20, y: 0))
path.addQuadCurve(to: CGPoint(x: self.viewTabBorder.center.x + self.btnHome.frame.size.width - 20, y: 0), controlPoint: CGPoint(x: self.viewTabBorder.center.x, y: self.btnHome.frame.height + 5))
path.addLine(to: CGPoint(x: self.viewTabBorder.center.x + self.btnHome.frame.size.width - 20, y: 0))
let line = CAShapeLayer()
line.path = path.cgPath
line.strokeColor = UIColor(red: 224, green: 224, blue: 224).cgColor
line.fillColor = UIColor.white.cgColor
self.view.layer.addSublayer(line)
self.viewTabBorder.layer.addSublayer(line)
}
Can anyone show me what i'm missing? Thank you very much in advance!


A couple of observations:
You are defining paths for sublayers to views. Doing this in
viewWillAppearis too early in the process. The layout engine may not be done applying constraints, placing the views in their final location. Do the defining of the paths inviewDidLayoutSubviews, not inviewWillAppear(norviewDidLoad, norviewWillLayoutSubviews).This also has the virtue that should you ever change your app to support multiple orientations or do any changes that affect the constraints in the view, this
viewDidLayoutSubviewswe be called again so these sublayers will be properly updated as needed. If you useviewWillAppearyour app will not support dynamic changes to your views (should you ever need that).That having been said, be aware that
viewDidLayoutSubviewscan be called multiple times. So you can, for example, add the sublayers inviewDidLoad, but update their paths inviewDidLayoutSubviews.It is likely not an issue here, but I would suggest you avoid using
frameorcenterwhen defining a path for a sublayer. Remember that these two properties are defined in the coordinate system of their superview. I’d suggest you always use a view’sbounds(defined in a view’s own coordinate system), not itsframe(defined in its superview’s coordinate system) when trying to define a path for a sublayer. And if you want, for example, the horizontal center of a view when defining a sublayer’s path, usebounds.midXnotcenter.x.Sometimes this won’t matter (if the view goes edge-to-edge within its superview), but (a) it suggests a misunderstanding of the different coordinate systems; and (b) if you ever add safe areas or otherwise adjust the placement of the view, using the
frame/centerwill get you into trouble.A slightly more advanced concept: Once you have your immediate issue behind you, you might consider moving this configuration of subviews into a
UIViewsubclass (using itslayoutSubviewsmethod), possibly even making it an@IBDesignableclass so you can see this rendered in Interface Builder. Or, another approach is to wrap this in a container view controller. But either way, this can help prevent view controller “bloat”. It’s probably a bit beyond the scope of this question, but something to consider as you move forward.