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 ....

+3


source to share


2 answers


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  
}

      

+3


source


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.

+1


source







All Articles