I am trying to draw onto a subview (subclass of UIView) that I have added to a UICollectionViewCell's contentView. I did this by implementing a drawRect function to draw a very simple cross in the center of the subview.
I've put in layout constraints to shift the subview slightly right into the contentView, and they appear to do what I want.
The problem is that when drawRect gets called, the cross doesn't appear in the center of the subview as intended. Rather, it gets shifted, by the same amount of shift the layout constraint tries to apply to the subview.
I used Apple's Implementing Modern Collection Views code as a base, and modified it as follows to implement the above.
In TextCell.swift (the custom cell implementation 'Cells and Supplementary Views'
class TextCellDrawingSubView: UIView {
override init(frame: CGRect) { super.init(frame: frame) }
required init?(coder: NSCoder) { fatalError("initWithCoder not implemnted") }
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
let crosshairLength = 10.0 / 2.0
let viewCenter = self.center // ** this was wrong, see below **
path.move(to:CGPoint(x:viewCenter.x - crosshairLength, y: viewCenter.y))
path.addLine(to:CGPoint(x:viewCenter.x + crosshairLength, y: viewCenter.y))
path.move(to:CGPoint(x:viewCenter.x,y: viewCenter.y - crosshairLength))
path.addLine(to:CGPoint(x:viewCenter.x, y: viewCenter.y + crosshairLength))
path.lineWidth = 1.0
UIColor.green.setStroke()
path.stroke()
}
class TextCell: UICollectionViewCell {
let label = UILabel()
let drawingSubView = TextCellDrawingSubView() //**this line added**
...
extension TextCell {
func configure() {
...
drawingSubView.translatesAutoresizingMaskIntoConstraints = false
drawingSubView.backgroundColor = .blue
drawingSubView.alpha = 0.4
// Position the drawingSubView at a specific offset left into the contentView.
NSLayoutConstraint.activate([
drawingSubView.widthAnchor.constraint(equalToConstant:50),
drawingSubView.heightAnchor.constraint(equalToConstant:50),
drawingSubView.topAnchor.constraint(equalTo:contentView.topAnchor),
drawingSubView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20.0)
])
//The cross doesn't appear in the center of the drawingSubView.
//It's as if the leadingAnchor constraint is applied twice, to the view, and then again to the cross.
}
}
The image below is what appears. I was expecting the (green) cross to be in the center of the dark blue subview, but it shifts right by the same offset that is specified by the leadingAnchor constraint, i.e. 20 points.
Edit: If I change the leadingAnchor constraint to just be equal to the contentView's leadingAnchor, i.e. change the constant from 20 to 0, the subview appears left-aligned in the contentView(s) - and the cross is centered in it.
(FWIW my ultimate objective is to draw 2 line diagrams into a cell, one with air temperatures in the last week, and one just below with the daily average atmospheric pressure in that time.)
I looked high and low in the Apple Developer documentation, watched WWDC Advances in UICollectionViews for 2019, 2020 and even 2016. No luck finding an explanation or a fix.
Edit: I am now reading through "Modern Autolayout" by K.Harrison again.
Edit: per the answer provided below by @DonMag:
In TextCell.swift, I replaced
let viewCenter = self.center
with
let viewCenter = CGPoint(x:self.bounds.midX, y:self.bounds.midY)
In your override func draw(_ rect: CGRect)
, you are referencing:
let viewCenter = self.center
If the frame of self
happens to be:
origin = (x = 20.0, y = 0.0)
size = (width = 50.0, height = 50.0)
then viewCenter.x
will equal 20.0 + 25.0
.
Change viewCenter
to:
let viewCenter = self.bounds.center
and you should get your desired alignment.