My team are implementing a fancy collection layout, where items change their size and offset as they are scrolled. We had already implemented it _sans_ ASDK by subclassing UICollectionViewLayout, roughly similar to this tutorial. Our subclass is called UltraVisualLayout. Its implementation is long and math-y, but the important bits basically go like this:
class UltravisualLayout: UICollectionViewLayout {
var cache = [UICollectionViewLayoutAttributes]()
override func prepareLayout() {
cache.removeAll(keepCapacity: false)
for item in 0..<numberOfItems {
// calculate x, y and h...
let frame = CGRect(x: x, y: y, width: width - (2 * x), height: h)
attributes.frame = frame
attributes.zIndex = item
cache.append(attributes)
}
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in cache {
if CGRectIntersectsRect(attributes.frame, rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
var layoutAttributes: UICollectionViewLayoutAttributes?
for attributes in cache {
if attributes.indexPath == indexPath {
layoutAttributes = attributes
}
}
return layoutAttributes
}
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
}
So basically, as the user scrolls, the layouts are continually invalidated, recalculated, and stored in a cache, from whence they are fetched upon the collection view's request.
ASDK is very cool, and so we decided to convert our collection view controller to an ASViewController with an ASCollectionNode. We init them as follows:
init {
super.init(node: ASCollectionNode(collectionViewLayout: UltraVisualLayout()))
// etc...
}
We subclassed ASCollectionViewLayoutInspecting to interact with the layout as follows:
class UltravisualLayoutInspector: NSObject, ASCollectionViewLayoutInspecting {
func collectionView(collectionView: ASCollectionView!, constrainedSizeForNodeAtIndexPath indexPath: NSIndexPath!) -> ASSizeRange {
return ASSizeRangeMake(CGSizeZero, layout.itemSizeAtIndexPath(indexPath))
}
func collectionView(collectionView: ASCollectionView!, constrainedSizeForSupplementaryNodeOfKind kind: String!, atIndexPath indexPath: NSIndexPath!) -> ASSizeRange {
return ASSizeRange(min: CGSizeZero, max: CGSizeZero)
}
func collectionView(collectionView: ASCollectionView!, numberOfSectionsForSupplementaryNodeOfKind kind: String!) -> UInt {
return 0
}
func collectionView(collectionView: ASCollectionView!, supplementaryNodesOfKind kind: String!, inSection section: UInt) -> UInt {
return 0
}
}
The layout still works on the UI side of things, apparently. Using XCode's visual inspector thingy, I can see that the frames are updating correctly (size and offset-wise). But when viewing the app itself, things are wrong. The display nodes do not update their sizes as they scroll. They do update their offsets though.
We think it might be because there is no ASDK equivalent to UICollectionViewLayout's shouldInvalidateLayoutForBoundsChange, so we can't notify the collection node that it should recalculate its display nodes' layout specs. We tried things like this
func scrollViewDidScroll(scrollView: UIScrollView) {
collectionNode.invalidateCalculatedLayout()
}
but to no effect.
How could we approach this?
We found that updating the frame of a view in a collection did not have the desired effect, but applying a transform to an item's attributes in the UICollectionViewLayout subclass works! We are now using something like
class UltravisualLayout: UICollectionViewLayout {
override func prepareLayout() {
cache.removeAll(keepCapacity: false)
for item in 0..<numberOfItems {
// calculate x, y and h...
let frame = CGRect(x: x, y: y, width: width - (2 * x), height: h)
attributes.frame = frame
attributes.zIndex = item
let scale = (width - (2 * x)) / width
attributes.transform = CGAffineTransformMakeScale(scale, scale) // this works
cache.append(attributes)
}
}
// ...
}
Going to close this out as it looks like it was figured out.
Most helpful comment
We found that updating the frame of a view in a collection did not have the desired effect, but applying a transform to an item's attributes in the
UICollectionViewLayoutsubclass works! We are now using something like