如何继承 UICollectionViewLayoutAttributes
How to subclass UICollectionViewLayoutAttributes
我正在尝试对 UICollectionViewLayoutAttributes
进行子类化,以便我可以添加一个额外的属性 points(CGPoints
的数组)。
该子类被设计为仅用作装饰视图的属性,因此我需要将成员 representedElementCategory
设置为 .Decoration
。但是 representedElementCategory
是只读的,设置它的唯一方法是通过便利初始化程序:
convenience init(forDecorationViewOfKind decorationViewKind: String, withIndexPath indexPath: NSIndexPath)
查看 Objective-C headers 我看到这个方便的初始化程序实际上被定义为工厂方法:
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath*)indexPath;
我希望我的子类的初始化程序看起来像:
CustomLayoutAttributes(points: [CGPoint], representedElementKind: String, withIndexPath: NSIndexPath)
但由于子类无法调用其 parents 的便捷初始化程序,我看不出这怎么可能。
我认为子类化的唯一方法是:
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)]
实际上这行不通,因为 'as!' 转换会失败....
UICollectionViewLayoutAttributes 子类化需要一些特性。
你有没有在 https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewLayoutAttributes_class/
检查子类
In most cases, you use this class as-is. If you want to supplement the base layout attributes with custom layout attributes, you can subclass and define whatever properties you want to store the additional layout data. Because layout attribute objects may be copied by the collection view, make sure your subclass conforms to the NSCopying protocol by implementing any methods appropriate 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 layout 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 if those attributes have not changed. It determines whether the attributes have changed by comparing the old and new attribute objects using the isEqual: method. Because the default implementation of this method checks only 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 all equal, call super and return the resulting value at the end of your implementation.
事实证明,sub class 可以调用 super class' 便利初始化程序,后者又可以调用 sub class' 初始化程序。例如:
CustomLayoutAttributes(forDecorationViewOfKind: "decoration1", withIndexPath: indexPath)
这里:
init(forDecorationViewOfKind: String, withIndexPath: NSIndexPath)
未在 CustomLayoutAttributes
class 中定义,但仍可用于构造子 class.
的实例
实现示例:
// 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
}
方法名称已更改,因此转换为 Swift 5:
class SceneSelectorAttributes: UICollectionViewLayoutAttributes {
var value = CGFloat()
override func copy(with zone: NSZone? = nil) -> Any {
let copy = super.copy(with: zone) as! SceneSelectorAttributes
copy.value = self.value
return copy
}
override func isEqual(_ object: Any?) -> Bool {
if let rhs = object as? SceneSelectorAttributes {
if value != rhs.value {
return false
}
return super.isEqual(object)
} else {
return false
}
}
}
我正在尝试对 UICollectionViewLayoutAttributes
进行子类化,以便我可以添加一个额外的属性 points(CGPoints
的数组)。
该子类被设计为仅用作装饰视图的属性,因此我需要将成员 representedElementCategory
设置为 .Decoration
。但是 representedElementCategory
是只读的,设置它的唯一方法是通过便利初始化程序:
convenience init(forDecorationViewOfKind decorationViewKind: String, withIndexPath indexPath: NSIndexPath)
查看 Objective-C headers 我看到这个方便的初始化程序实际上被定义为工厂方法:
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath*)indexPath;
我希望我的子类的初始化程序看起来像:
CustomLayoutAttributes(points: [CGPoint], representedElementKind: String, withIndexPath: NSIndexPath)
但由于子类无法调用其 parents 的便捷初始化程序,我看不出这怎么可能。
我认为子类化的唯一方法是:
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)]
实际上这行不通,因为 'as!' 转换会失败....
UICollectionViewLayoutAttributes 子类化需要一些特性。 你有没有在 https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewLayoutAttributes_class/
检查子类In most cases, you use this class as-is. If you want to supplement the base layout attributes with custom layout attributes, you can subclass and define whatever properties you want to store the additional layout data. Because layout attribute objects may be copied by the collection view, make sure your subclass conforms to the NSCopying protocol by implementing any methods appropriate 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 layout 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 if those attributes have not changed. It determines whether the attributes have changed by comparing the old and new attribute objects using the isEqual: method. Because the default implementation of this method checks only 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 all equal, call super and return the resulting value at the end of your implementation.
事实证明,sub class 可以调用 super class' 便利初始化程序,后者又可以调用 sub class' 初始化程序。例如:
CustomLayoutAttributes(forDecorationViewOfKind: "decoration1", withIndexPath: indexPath)
这里:
init(forDecorationViewOfKind: String, withIndexPath: NSIndexPath)
未在 CustomLayoutAttributes
class 中定义,但仍可用于构造子 class.
实现示例:
// 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
}
方法名称已更改,因此转换为 Swift 5:
class SceneSelectorAttributes: UICollectionViewLayoutAttributes {
var value = CGFloat()
override func copy(with zone: NSZone? = nil) -> Any {
let copy = super.copy(with: zone) as! SceneSelectorAttributes
copy.value = self.value
return copy
}
override func isEqual(_ object: Any?) -> Bool {
if let rhs = object as? SceneSelectorAttributes {
if value != rhs.value {
return false
}
return super.isEqual(object)
} else {
return false
}
}
}