I am trying to calibrate a UIView to stay on centre as it rotates. This follows up a problem identified by the best answers to my previous question. I have since devised code based on one answer proposing to use viewWillAppear
or viewDidLayoutSubviews
instead of viewDidLoad
.
As I am new to Swift I had to revisit the Swift tutorial Implement a Custom Control which describes how to make a subClass of UIView. My code can draw a circle using any one of three functions: viewWillAppear
, viewDidLayoutSubviews
or viewDidLoad
.
However when I render the code, the circle doesn’t always remain on centre as the screen rotates. Below are successive rotations that happen using viewDidLoad
or viewWillAppear
When the app is launched on a screen in portrait orientation the problem appears immediately after the first rotation irrespective of left or right. If launched in landscape orientation it is possible to miss a problem if one has not tried at least four successive rotations.
Below are successive rotations that happen using viewDidLayoutSubviews
where rotation occasionally results in an on-centre circle overlaying an off-centre circle. This can happen after several rotations but one must complete at least four rotations in order to notice a problem.
Here is the code for CircleView
- my attempt to make a subclass of UIView
import UIKit
class CircleView: UIView {
let radius = CGFloat(200)
let x: CGFloat = 0
let y: CGFloat = 0
// MARK: Initialization
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
func setup() {
// Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
// The Bezier path that we made needs to be converted
// to a CGPath before it can be used on a layer.
shapeLayer.path = createBezierPath().cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 8.0
// add the new layer to our custom view
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {
// create a new path
let path = UIBezierPath(arcCenter: CGPoint(x: x, y: y),
radius: radius,
startAngle: CGFloat(M_PI * -0.5),
endAngle:CGFloat(M_PI * 1.5),
clockwise: true)
path.fill()
path.stroke()
return path
}
}
and here is the code for ViewController
import UIKit
class ViewController: UIViewController {
var circle: CircleView?
var x: CGFloat?
var y: CGFloat?
override func viewDidLoad() {
super.viewDidLoad()
let centre = centreCircle()
// create a new UIView and add it to the view controller
let circleView = CircleView()
circleView.frame = CGRect(x: centre.x, y: centre.y, width: 0, height: 0)
view.addSubview(circleView)
}
func centreCircle() -> CGPoint {
circle?.bounds = UIScreen.main.bounds
return CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY)
}
}
And here is code for the UIScreen extension that returns the screen's centre point
import UIKit
extension UIScreen {
var centre: CGPoint {
return CGPoint(x: bounds.midX, y: bounds.midY)
}
}
To produce the above rotations I replaced viewDidLoad
function with viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let centre = centreCircle()
// create a new UIView and add it to the view controller
let circleView = CircleView()
circleView.frame = CGRect(x: centre.x, y: centre.y, width: 0, height: 0)
view.addSubview(circleView)
}
or viewDidLayoutSubviews
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let centre = centreCircle()
// create a new UIView and add it to the view controller
let circleView = CircleView()
circleView.frame = CGRect(x: centre.x, y: centre.y, width: 0, height: 0)
view.addSubview(circleView)
}
Can anyone see a problem in my code that might be stopping rotation from working ?