I have an UIImageView
to rotate in a draw
function.
public var progress: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override public func draw(_ rect: CGRect) {
rotateIcon(by: progress)
}
private func rotateIcon(by angle: CGFloat) {
if imageView == nil {
imageView = UIImageView(image: UIImage(named: "bt_info"))
imageView?.center = center
imageView?.frame.size = CGSize(width: 24.0, height: 24.0)
addSubview(imageView!)
}
imageView?.transform = CGAffineTransform(rotationAngle: angle)
}
Below is the output attached for the above snippet.
When I set the frame size, the imageView
shrink and expands as it rotates. I would like to maintain the size as the imageView
rotates. What am I missing?
OK - the code you posted in your question does not match the code in your Dropbox project...
In your project, you're doing this:
private func rotateScannerIcon(by angle: CGFloat) {
if testView == nil {
testView = UIView()
testView?.backgroundColor = .red
addSubview(testView!)
}
let centerPoint = CGPoint(x: bounds.midX, y: bounds.midY)
testView?.center = centerPoint
testView?.frame.size = CGSize(width: 24.0, height: 24.0)
testView?.transform = CGAffineTransform(rotationAngle: angle)
}
With that code, you are changing the frame size after a rotation transform has been applied... and the actual view is no longer "square."
If you move the position / size code inside your "create" block:
private func rotateScannerIcon(by angle: CGFloat) {
if testView == nil {
testView = UIView()
testView?.backgroundColor = .red
addSubview(testView!)
let centerPoint = CGPoint(x: bounds.midX, y: bounds.midY)
testView?.center = centerPoint
testView?.frame.size = CGSize(width: 24.0, height: 24.0)
}
testView?.transform = CGAffineTransform(rotationAngle: angle)
}
The "squeezing" of the frame no longer happens.
Overriding draw()
is probably not the best approach, though. That is primarily done when UIKit view updates are not sufficient to handle the layout - such as when using stroke/fill on paths.
So, I would suggest a couple changes.
Create your subview (UIView
or UIImageView
or whatever) when the custom view subclass is initialized, and size/position it with auto-layout constraints.
Then, when you update the progress
property, apply the rotation transform.
As an example, here's a modified version of your TestView
class:
public class TestView: UIView {
public var progress: CGFloat = 0 {
didSet {
rotateScannerIcon(by: (progress * CGFloat(Double.pi)))
}
}
internal var testView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// create the view (or image view)
testView = UIView()
testView.backgroundColor = .red
// add it to self
addSubview(testView)
// let's use auto-layout constraints
testView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// constrain the view to 24 x 24, centered in self
testView.widthAnchor.constraint(equalToConstant: 24.0),
testView.heightAnchor.constraint(equalTo: testView.widthAnchor),
testView.centerXAnchor.constraint(equalTo: centerXAnchor),
testView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
private func rotateScannerIcon(by angle: CGFloat) {
testView.transform = CGAffineTransform(rotationAngle: angle)
}
}