HelloTimo
HelloTimo

Reputation: 578

How to downcast custom UICollectionViewLayoutAttributes to apply() method?

I just can´t find out how to get access to custom layout attributes in the apply() method of a custom cell.

I have to implement a custom layout attribute to my CollectionViewLayoutAtrributes, so I subclassed them. This works well so far:

class TunedLayoutAttributes: UICollectionViewLayoutAttributes {

    var customLayoutAttributeValue: CGFloat = 1000

    override func copy(with zone: NSZone?) -> Any {
        let copy = super.copy(with: zone) as! TunedLayoutAttributes
        customLayoutAttributeValue = customLayoutAttributeValue
        return copy
    }

    override func isEqual(_ object: Any?) -> Bool {
        if let attributes = object as? TunedLayoutAttributes {
            if attributes. customLayoutAttributeValue == customLayoutAttributeValue {
                return super.isEqual (object)
            }
        }
        return false
    }
}

The value has to dynamically change based on user scroll interaction. Now I need my custom cells to update their appearance after an invalidateLayout call from the custom UICollectionViewLayout class. To my knowledge this usually can also be done by overriding the cells classes apply(_ layoutAttributes: UICollectionViewLayoutAttributes) method.

Usually like so:

override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {

    let newFrame = // calculations here. CGRect(....) 
    layoutAttributes.frame = newFrame
}




Now the missing chemistry:

Unlike in the apply() example above my new customLayoutAttributeValue is (of course?) not part of the layoutAttributes: in the method.

So I tried to downcast the layoutAttributes to my custom class like so:

override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {

    let tunedLayoutAttributes = layoutAttributes as! TunedLayoutAttributes
    tunedLayoutAttributes.customLayoutAttributeValue = // calculation here. 
}

So how do I get access to tunedLayoutAttributes in the apply() method? Any help is appreciated. Thanks for reading!

Upvotes: 2

Views: 1212

Answers (1)

CodingMeSwiftly
CodingMeSwiftly

Reputation: 3261

You kind of gave the solution to yourself here. UICollectionViewLayoutAttributes does (of course) not know about customLayoutAttributeValue, so you have to cast to the appropriate class (TunedLayoutAttributes, in your case).

Now in order for apply(_:) to actually give you TunedLayoutAttributes and not just plain UICollectionViewLayoutAttributes, you need to tell your UICollectionViewLayout subclass to use your custom class (TunedLayoutAttributes) when vending layout attributes for items in the layout.

You do that by overriding class var layoutAttributesClass in you layout subclass.

Note that if you override layout attributes vending methods (layoutAttributesForElements(in:) and friends) in your layout subclass, you'd need to return TunedLayoutAttributes there for the whole thing to work.

Also note that UIKit frequently copies attributes under the hood when performing collection view layout passes, so make sure your copy and isEqual methods are working as expected. (e.g., the attributes objects passed to apply(_:) are not (necessarily) the objects your layout vended, but rather copies.

Edit

As discussed in the comments, you should replace the forcecast as! with an if let as? cast to prevent crashes when apply(_:) actually gets passed plain UICollectionViewLayoutAttributes.

Upvotes: 4

Related Questions