How to subclass UICollectionViewLayoutAttributes
I am trying to subclass UICollectionViewLayoutAttributes
to add additional attribute points (array CGPoints
).
The subclass is only intended to be used as attributes for decoration views, so I need to set the element representedElementCategory
to .Decoration
. But representedElementCategory
it's read-only, and the only way to set it is through a convenience initializer:
convenience init(forDecorationViewOfKind decorationViewKind: String, withIndexPath indexPath: NSIndexPath)
Looking at the Objective-C headers, I can see that this convenience initializer is actually defined as a factory method:
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath*)indexPath;
I would like my subclass initializer to look like this:
CustomLayoutAttributes(points: [CGPoint], representedElementKind: String, withIndexPath: NSIndexPath)
But since subclasses cannot call their parent's convenience initializers, I don't see how this would be possible.
Am I correct in thinking that the only way to subclass is:
class CustomLayoutAttributes: UICollectionViewLayoutAttributes {
var points = [CGPoint]()
}
let attribs = UICollectionViewLayoutAttributes(
forDecorationViewOfKind: "x",
withIndexPath: NSIndexPath(forItem: 0, inSection: 0)
) as! CustomLayoutAttributes
attribs.points = [CGPoint(x: 1.0, y: 2.0), CGPoint(x: 4.0, y: 5.0)]
It won't actually work because the "how!" cast will fail ....
source to share
It turns out that the subclass can call the super class convenience initializer, which in turn can call the subclass's initializer. For example:
CustomLayoutAttributes(forDecorationViewOfKind: "decoration1", withIndexPath: indexPath)
Here:
init(forDecorationViewOfKind: String, withIndexPath: NSIndexPath)
not defined in the class CustomLayoutAttributes
, but can be used to construct an instance of a subclass.
Implementation example:
// CustomLayoutAttributes.swift
class CustomLayoutAttributes: UICollectionViewLayoutAttributes {
// Additional attribute to test our custom layout
var points = [CGPoint]()
// MARK: NSCopying
override func copyWithZone(zone: NSZone) -> AnyObject {
let copy = super.copyWithZone(zone) as! CustomLayoutAttributes
copy.points = self.points
return copy
}
override func isEqual(object: AnyObject?) -> Bool {
if let rhs = object as? CustomLayoutAttributes {
if points != rhs.points {
return false
}
return super.isEqual(object)
} else {
return false
}
}
}
// CustomLayout.swift
override func layoutAttributesForItemAtIndexPath(path: NSIndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = CustomLayoutAttributes(forDecorationViewOfKind: "decoration1", withIndexPath: indexPath)
attributes.points = [...]
return attributes
}
source to share
Some functionality is required to subclass UICollectionViewLayoutAttributes. You are checking for subclasses not at https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewLayoutAttributes_class/
In most cases, you will use this class as is. If you want to supplement the attributes of the base layout with custom layout attributes, you can subclass and define all the properties that you want to preserve additional layout data. Since layout attribute objects can be copied as a collection, make sure your subclass conforms to the NSCopying protocol by implementing any methods suitable for copying your custom attributes to new instances of your subclass. In addition to defining your subclass, your UICollectionReusableView objects need to implement the applyLayoutAttributes: method so that they can apply any custom attributes at link time.
If you subclass and implement any custom layout attributes, you must also override the inherited isEqual: method to compare the values โโof your properties. In iOS 7 and later, the collection view does not apply layout attributes unless those attributes have changed. It determines if the attributes have changed by comparing the old and new attribute objects using the isEqual: method. Since the default implementation of this method only checks the existing properties of this class, you must implement your own version of the method to compare any additional properties. If your custom properties are equal, call super and return the resulting value at the end of your implementation.
source to share