CustomCollectionViewLayout 在 Swift 3 上不起作用
Posted
技术标签:
【中文标题】CustomCollectionViewLayout 在 Swift 3 上不起作用【英文标题】:CustomCollectionViewLayout not working on Swift 3 【发布时间】:2016-12-14 04:16:53 【问题描述】:我已经实现了UICollectionView
,它可以使用下面的示例使用粘性行和列进行水平和垂直滚动:
Example
Github
应用程序可以在 Swift2
上找到,但升级后 Swift3
CustomCollectionViewLayout
给我错误
class CustomCollectionViewLayout: UICollectionViewLayout
let numberOfColumns = 8
var itemAttributes : NSMutableArray!
var itemsSize : NSMutableArray!
var contentSize : CGSize!
override func prepare()
if self.collectionView?.numberOfSections == 0
return
if (self.itemAttributes != nil && self.itemAttributes.count > 0)
for section in 0..<self.collectionView!.numberOfSections
let numberOfItems : Int = self.collectionView!.numberOfItems(inSection: section)
for index in 0..<numberOfItems
if section != 0 && index != 0
continue
let attributes : UICollectionViewLayoutAttributes = self.layoutAttributesForItem(at: IndexPath(item: index, section: section))
if section == 0
var frame = attributes.frame
frame.origin.y = self.collectionView!.contentOffset.y
attributes.frame = frame
if index == 0
var frame = attributes.frame
frame.origin.x = self.collectionView!.contentOffset.x
attributes.frame = frame
return
if (self.itemsSize == nil || self.itemsSize.count != numberOfColumns)
self.calculateItemsSize()
var column = 0
var xOffset : CGFloat = 0
var yOffset : CGFloat = 0
var contentWidth : CGFloat = 0
var contentHeight : CGFloat = 0
for section in 0..<self.collectionView!.numberOfSections
let sectionAttributes = NSMutableArray()
for index in 0..<numberOfColumns
let itemSize = (self.itemsSize[index] as AnyObject).cgSizeValue
let indexPath = IndexPath(item: index, section: section)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = CGRect(x: xOffset, y: yOffset, width: (itemSize?.width)!, height: (itemSize?.height)!).integral
if section == 0 && index == 0
attributes.zIndex = 1024;
else if section == 0 || index == 0
attributes.zIndex = 1023
if section == 0
var frame = attributes.frame
frame.origin.y = self.collectionView!.contentOffset.y
attributes.frame = frame
if index == 0
var frame = attributes.frame
frame.origin.x = self.collectionView!.contentOffset.x
attributes.frame = frame
sectionAttributes.add(attributes)
xOffset += (itemSize?.width)!
column += 1
if column == numberOfColumns
if xOffset > contentWidth
contentWidth = xOffset
column = 0
xOffset = 0
yOffset += (itemSize?.height)!
if (self.itemAttributes == nil)
self.itemAttributes = NSMutableArray(capacity: self.collectionView!.numberOfSections)
self.itemAttributes .add(sectionAttributes)
let attributes : UICollectionViewLayoutAttributes = (self.itemAttributes.lastObject as AnyObject).lastObject as! UICollectionViewLayoutAttributes
contentHeight = attributes.frame.origin.y + attributes.frame.size.height
self.contentSize = CGSize(width: contentWidth, height: contentHeight)
override var collectionViewContentSize : CGSize
return self.contentSize
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes!
return self.itemAttributes[indexPath.section][indexPath.row] as! UICollectionViewLayoutAttributes
// Error in above return line and error is Type 'Any' has no subscript members
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
var attributes = [UICollectionViewLayoutAttributes]()
if self.itemAttributes != nil
for section in self.itemAttributes
let filteredArray = (section as AnyObject).filtered(
using: NSPredicate(block: (evaluatedObject, bindings) -> Bool in
return rect.intersects(evaluatedObject.frame)
// Error in above return line and error is Value of type 'Any?' has no member 'frame'
)
) as! [UICollectionViewLayoutAttributes]
attributes.append(contentsOf: filteredArray)
return attributes
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
return true
func sizeForItemWithColumnIndex(_ columnIndex: Int) -> CGSize
var text : String = ""
switch (columnIndex)
case 0:
text = "Col 0"
case 1:
text = "Col 1"
case 2:
text = "Col 2"
case 3:
text = "Col 3"
case 4:
text = "Col 4"
case 5:
text = "Col 5"
case 6:
text = "Col 6"
default:
text = "Col 7"
let size : CGSize = (text as NSString).size(attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 17.0)])
let width : CGFloat = size.width + 25
return CGSize(width: width, height: 30)
func calculateItemsSize()
self.itemsSize = NSMutableArray(capacity: numberOfColumns)
for index in 0..<numberOfColumns
self.itemsSize.add(NSValue(cgSize: self.sizeForItemWithColumnIndex(index)))
ContentCollectionViewCell
class ContentCollectionViewCell: UICollectionViewCell
@IBOutlet weak var contentLabel: UILabel!
override func awakeFromNib()
super.awakeFromNib()
// Initialization code
DateCollectionViewCell
class DateCollectionViewCell: UICollectionViewCell
@IBOutlet weak var dateLabel: UILabel!
override func awakeFromNib()
super.awakeFromNib()
// Initialization code
CollectionViewController
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate
let dateCellIdentifier = "DateCellIdentifier"
let contentCellIdentifier = "ContentCellIdentifier"
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad()
super.viewDidLoad()
self.collectionView .register(UINib(nibName: "DateCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: dateCellIdentifier)
self.collectionView .register(UINib(nibName: "ContentCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: contentCellIdentifier)
// MARK - UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int
return 5
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 8
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
print(indexPath.description)
if indexPath.section == 0
if indexPath.row == 0
let dateCell : DateCollectionViewCell = collectionView .dequeueReusableCell(withReuseIdentifier: dateCellIdentifier, for: indexPath) as! DateCollectionViewCell
dateCell.backgroundColor = UIColor.white
dateCell.dateLabel.font = UIFont.systemFont(ofSize: 13)
dateCell.dateLabel.textColor = UIColor.black
dateCell.dateLabel.text = "Donor ID"
return dateCell
else
let contentCell : ContentCollectionViewCell = collectionView .dequeueReusableCell(withReuseIdentifier: contentCellIdentifier, for: indexPath) as! ContentCollectionViewCell
contentCell.contentLabel.font = UIFont.systemFont(ofSize: 13)
contentCell.contentLabel.textColor = UIColor.black
contentCell.contentLabel.text = "Section"
if indexPath.section % 2 != 0
contentCell.backgroundColor = UIColor(white: 242/255.0, alpha: 1.0)
else
contentCell.backgroundColor = UIColor.white
return contentCell
else
if indexPath.row == 0
let dateCell : DateCollectionViewCell = collectionView .dequeueReusableCell(withReuseIdentifier: dateCellIdentifier, for: indexPath) as! DateCollectionViewCell
dateCell.dateLabel.font = UIFont.systemFont(ofSize: 13)
dateCell.dateLabel.textColor = UIColor.black
dateCell.dateLabel.text = String(indexPath.section)
if indexPath.section % 2 != 0
dateCell.backgroundColor = UIColor(white: 242/255.0, alpha: 1.0)
else
dateCell.backgroundColor = UIColor.white
return dateCell
else
let contentCell : ContentCollectionViewCell = collectionView .dequeueReusableCell(withReuseIdentifier: contentCellIdentifier, for: indexPath) as! ContentCollectionViewCell
contentCell.contentLabel.font = UIFont.systemFont(ofSize: 13)
contentCell.contentLabel.textColor = UIColor.black
contentCell.contentLabel.text = "Section:\(indexPath.section.description) / Row: \(indexPath.row.description)"
if indexPath.section % 2 != 0
contentCell.backgroundColor = UIColor(white: 242/255.0, alpha: 1.0)
else
contentCell.backgroundColor = UIColor.white
return contentCell
谢谢
【问题讨论】:
您在哪里遇到错误以及错误是什么,请使用该详细信息编辑您的问题。 @NiravD 第一个错误是返回 self.itemAttributes[indexPath.section][indexPath.row] as! UICollectionViewLayoutAttributes // 错误:'Any' 类型没有下标成员 第二个错误是返回 rect.intersects(evaluationObject.frame) // 错误:'Any' 类型的值?没有成员“框架” @NiravD 如果您滚动编码部分,您可以找到它我在评论中提到了错误... 我已经告诉你编辑你的问题,请编辑你的问题而不是在评论中添加错误。 @NiravD 对不起,如果滚动代码有错误行,我已经提到了编码部分的错误。 【参考方案1】:你好用这个替换你的CustomCollectionViewLayout
CustomCollectionViewLayout.swift
class CustomCollectionViewLayout: UICollectionViewLayout
let numberOfColumns = 8
var itemAttributes : NSMutableArray!
var itemsSize : NSMutableArray!
var contentSize : CGSize!
override func prepare()
if self.collectionView?.numberOfSections == 0
return
if (self.itemAttributes != nil && self.itemAttributes.count > 0)
for section in 0..<self.collectionView!.numberOfSections
let numberOfItems : Int = self.collectionView!.numberOfItems(inSection: section)
for index in 0..<numberOfItems
if section != 0 && index != 0
continue
let attributes : UICollectionViewLayoutAttributes = self.layoutAttributesForItem(at: IndexPath(item: index, section: section))
if section == 0
var frame = attributes.frame
frame.origin.y = self.collectionView!.contentOffset.y
attributes.frame = frame
if index == 0
var frame = attributes.frame
frame.origin.x = self.collectionView!.contentOffset.x
attributes.frame = frame
return
if (self.itemsSize == nil || self.itemsSize.count != numberOfColumns)
self.calculateItemsSize()
var column = 0
var xOffset : CGFloat = 0
var yOffset : CGFloat = 0
var contentWidth : CGFloat = 0
var contentHeight : CGFloat = 0
for section in 0..<self.collectionView!.numberOfSections
let sectionAttributes = NSMutableArray()
for index in 0..<numberOfColumns
let itemSize = (self.itemsSize[index] as AnyObject).cgSizeValue
let indexPath = IndexPath(item: index, section: section)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = CGRect(x: xOffset, y: yOffset, width: (itemSize?.width)!, height: (itemSize?.height)!).integral
if section == 0 && index == 0
attributes.zIndex = 1024;
else if section == 0 || index == 0
attributes.zIndex = 1023
if section == 0
var frame = attributes.frame
frame.origin.y = self.collectionView!.contentOffset.y
attributes.frame = frame
if index == 0
var frame = attributes.frame
frame.origin.x = self.collectionView!.contentOffset.x
attributes.frame = frame
sectionAttributes.add(attributes)
xOffset += (itemSize?.width)!
column += 1
if column == numberOfColumns
if xOffset > contentWidth
contentWidth = xOffset
column = 0
xOffset = 0
yOffset += (itemSize?.height)!
if (self.itemAttributes == nil)
self.itemAttributes = NSMutableArray(capacity: self.collectionView!.numberOfSections)
self.itemAttributes .add(sectionAttributes)
let attributes : UICollectionViewLayoutAttributes = (self.itemAttributes.lastObject as AnyObject).lastObject as! UICollectionViewLayoutAttributes
contentHeight = attributes.frame.origin.y + attributes.frame.size.height
self.contentSize = CGSize(width: contentWidth, height: contentHeight)
override var collectionViewContentSize : CGSize
return self.contentSize
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes!
//return self.itemAttributes[indexPath.section][indexPath.row] as! UICollectionViewLayoutAttributes
let arr = self.itemAttributes[indexPath.section] as! NSMutableArray
return arr[indexPath.row] as! UICollectionViewLayoutAttributes
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
var attributes = [UICollectionViewLayoutAttributes]()
if self.itemAttributes != nil
for section in self.itemAttributes
let filteredArray = (section as! NSMutableArray).filtered(
using: NSPredicate(block: (evaluatedObject , bindings) -> Bool in
let a = evaluatedObject as! UICollectionViewLayoutAttributes
return rect.intersects(a.frame)
)
) as! [UICollectionViewLayoutAttributes]
attributes.append(contentsOf: filteredArray)
return attributes
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
return true
func sizeForItemWithColumnIndex(_ columnIndex: Int) -> CGSize
var text : String = ""
switch (columnIndex)
case 0:
text = "Col 0"
case 1:
text = "Col 1"
case 2:
text = "Col 2"
case 3:
text = "Col 3"
case 4:
text = "Col 4"
case 5:
text = "Col 5"
case 6:
text = "Col 6"
default:
text = "Col 7"
let size : CGSize = (text as NSString).size(attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 17.0)])
let width : CGFloat = size.width + 25
return CGSize(width: width, height: 30)
func calculateItemsSize()
self.itemsSize = NSMutableArray(capacity: numberOfColumns)
for index in 0..<numberOfColumns
self.itemsSize.add(NSValue(cgSize: self.sizeForItemWithColumnIndex(index)))
【讨论】:
【参考方案2】:我正在使用https://github.com/bartjacobs/StickyHeadersCollectionView
对于Swift 4 with xCode 9.2
,我的问题是Swift 3 @objc inference
设置为默认或关闭时StickyHeadersCollectionViewFlowLayout
停止工作(将其设置为打开问题就消失了)但由于Swift 3 @objc inference
是应该解决的问题,所以我采取了深入研究发现我只需要在UICollectionViewDelegate
方法上使用@objc
(对我来说,更准确地说只是最后一个):
// MARK: - Collection View Delegate Flow Layout Methods
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
return CGSize(width: collectionView.bounds.width, height: self.gridCellHeight)
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
return UIEdgeInsets.zero
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat
return 0.0
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
return 0.0
@objc func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize
if useStickyHeader
return CGSize(width: collectionView.bounds.width, height: self.gridCellHeight)
else
return CGSize.zero
【讨论】:
以上是关于CustomCollectionViewLayout 在 Swift 3 上不起作用的主要内容,如果未能解决你的问题,请参考以下文章