I am experimenting with my own custom layout in NSCollectionView on macOS. Whenever I run my app, I get a layout that is definitely not what I have computed (verified this through lots of calls to print()). The cells are shifted down by some seemingly arbitrary amount.
However, when I widen the window, the collection view automatically corrects itself, and the layout is what I expect. See the gif demonstration and my custom layout code. What is going on?
[Update] - I have determined the issue is partly due to the fact that my item views have Autoresizing Masks, and these layout constraints do not get modified until the view is widened. But why is that the case? Shouldn't the NSCollectionView follow the position and size I am giving it in my custom layout?
class MyLayout: NSCollectionViewLayout {
var attr: [NSCollectionViewLayoutAttributes] = []
override var collectionViewContentSize: NSSize {
get {
if self.collectionView?.frame == nil {
return .zero
}
return NSSize(width: CGFloat(self.collectionView?.frame.width ?? 0), height: CGFloat(self.collectionView?.numberOfSections ?? 0) * 75)
}
}
override func prepare() {
super.prepare()
print("prepare() \(String(describing: collectionView?.frame))")
self.attr = []
let height = CGFloat(75)
for s in 0..<(collectionView?.numberOfSections ?? 0) {
let width = CGFloat((collectionView?.frame.width ?? 0)) / CGFloat((collectionView?.numberOfItems(inSection: s) ?? 1))
for i in 0..<(collectionView?.numberOfItems(inSection: s) ?? 0) {
let attr = NSCollectionViewLayoutAttributes(forItemWith: IndexPath(arrayLiteral: s, i))
var shiftedOrigin = collectionView?.frame.origin ?? .zero
shiftedOrigin = shiftedOrigin.applying(CGAffineTransform(translationX: width * CGFloat(i), y: CGFloat(s) * height))
attr.frame = NSRect(origin: shiftedOrigin, size: NSSize(width: width, height: height))
print(attr.frame)
self.attr.append(attr)
}
}
}
override func layoutAttributesForElements(in rect: NSRect) -> [NSCollectionViewLayoutAttributes] {
print("layout attributes for elements IN \(rect)")
var returnAttr: [NSCollectionViewLayoutAttributes] = []
for attr in attr {
if rect.intersects(attr.frame) {
returnAttr.append(attr)
}
}
collectionView?.updateConstraints()
return returnAttr
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> NSCollectionViewLayoutAttributes? {
print("layout attributes for elements AT \(indexPath)")
for attr in attr {
if attr.indexPath == indexPath {
return attr
}
}
return nil
}
override func shouldInvalidateLayout(forBoundsChange newBounds: NSRect) -> Bool {
true
}
}
