I drew a rectangle view with an arrow pointing to a label above the view using UIBezierPath and CAShapeLayer.
I want the arrow to point to the exact center of the "P" label and iPhone11 simulator shows the result I want. However, when I run on iPhone8 simulator, the arrow doesn't point to the center of the label.
Why is the arrow pointing is little off to the right of the label on iPhone8 simulator?
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel! //horizontally and vertically aligned
@IBOutlet weak var pointingView: UIView! // leading & trailing constraints: 0, height:200
override func viewDidLoad() {
super.viewDidLoad()
drawPointingView()
}
private func drawPointingView() {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: pointingView.frame.height))
path.addLine(to: CGPoint(x: pointingView.frame.width, y: pointingView.frame.height))
path.addLine(to: CGPoint(x: pointingView.frame.width, y: 0))
//draw an arrow pointing out to the center cordinate of the label.
path.addLine(to: CGPoint(x: label.center.x + 15, y: 0))
path.addLine(to: CGPoint(x: label.center.x, y: -18))
path.addLine(to: CGPoint(x: label.center.x - 15, y: 0))
path.close()
let shape = CAShapeLayer()
shape.fillColor = UIColor.blue.cgColor
shape.path = path.cgPath
shape.strokeColor = UIColor.lightGray.cgColor
shape.lineWidth = 1
pointingView.layer.addSublayer(shape)
}
}
It's because you're doing the drawing in viewDidLoad
, and viewDidLoad
is too soon. All those values you are relying on, stuff like pointingView.frame.width
, are not known yet. You have to wait until after they are known. Otherwise you'll just be drawing in the wrong place (as in fact you are).
Things like that are the result of layout, so you have to wait until after viewDidLayoutSubviews
has been called for the first time.