我正在尝试创建一个表示网格的UICollectionView
,我想在顶部边缘创建一个标尺(浮动标题视图),它在y轴上浮动。
所以,就像在UITableView
中一样,这个视图基本上应该只是滚动屏幕顶部的内容。我希望这些描述足够清楚。对于理论而言,让我们进入实践部分!
以下是我现在正在使用的内容:
GridRulerView
,它是的子类
UICollectionReusableView
带有集合视图。我实施了viewForSupplementaryElementOfKind::
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView
{
var reusableHeaderView : UICollectionReusableView!
if kind == GridRulerView.Kind{
reusableHeaderView = collectionView.dequeueReusableSupplementaryViewOfKind(GridRulerView.Kind, withReuseIdentifier: GridRulerView.Kind, forIndexPath: indexPath) as! UICollectionReusableView
}
return reusableHeaderView
}
我有一个自定义UICollectionViewLayout
,其中包含以下内容
layoutAttributesForElementsInRect:
方法:
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
var attribs = [UICollectionViewLayoutAttributes]()
for (indexPath, attrs) in self.attributes{
if CGRectIntersectsRect(rect, attrs.frame) {
attribs.append(attrs)
}
}
var headerAttributes = GridViewRulerAttributes(forSupplementaryViewOfKind: GridRulerView.Kind, withIndexPath: NSIndexPath(forItem: 0, inSection: 0))
headerAttributes.zIndex = 500
headerAttributes.frame = CGRectMake(0, self.collectionView!.contentOffset.y, self.preCalculatedContentSize.width, 80)
attribs.append(headerAttributes)
return attribs
}
当我启动应用程序时,一切看起来都很棒,正是我想要的。 但是只要我将collectionView滚动到它的边界之外(collectionView的contentOffset =(-0.5,-0.5)< - 例如任何负值)我就崩溃了:
***断言失败 - [UICollectionViewData validateLayoutInRect:],/ SourceCache / UIKit / UIKit-3318.16.25 / UICollectionViewData.m:426
有趣的是,如果我将标题视图属性的框架的y属性设置为0而不是变量contentOffset.y(变为负数),一切正常。
有人知道为什么会这样吗?
以下是更具信息性的错误消息:
***由于未捕获的异常'NSInternalInconsistencyException'而终止应用程序,原因:'索引路径({length = 2,path = 0 - 0})的补充项的布局属性从索引路径更改:({length = 2 ,path = 0 - 0});元素种类:(GridRulerViewKind); frame =(0 0; 425.227 24); zIndex = 500;索引路径:({length = 2,path = 0 - 0});元素种类:(GridRulerViewKind); frame =(0 -5; 425.227 24); zIndex = 500;没有使布局'
无效
答案 0 :(得分:1)
哇,实际上是残疾人"所有异常"断点并得到一个更具信息性的错误:
***由于未捕获的异常终止应用程序' NSInternalInconsistencyException',原因:'索引路径({length = 2,path = 0 - 0})上的补充项目的布局属性已从索引路径:({length = 2,path = 0 - 0});元素种类:(GridRulerViewKind); frame =(0 0; 425.227 24); zIndex = 500;索引路径:({length = 2,path = 0 - 0});元素种类:(GridRulerViewKind); frame =(0 -5; 425.227 24); zIndex = 500;没有使布局失效'
现在,我仍然不知道该做什么或在哪里
"使布局无效"
然后在浏览类似的问题时,我在SO上遇到了类似的问题,我偶然发现了这个简单的功能:
<击> 撞击>
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool{
return true
}
果然,这似乎有效!不再崩溃了!
击>
实际上,这会导致集合视图布局在每个卷轴上失效,所以这似乎不是我正在寻找的解决方案......
经过一番研究后,我不认为有可能创建一个自行更改而不会使布局无效的补充视图(哪种看似完全合乎逻辑 - 布局如何知道更新标尺没有明确说明的观点?)。并且更新每个卷轴上的布局太昂贵了。
所以我想我将创建一个单独的视图,它完全独立于集合视图,只是简单地收听集合视图的滚动委托并根据滚动偏移调整其框架。我认为这种方法在性能方面更有效,同样易于实现。
答案 1 :(得分:1)
解决方案
只需检查presentedElementKind
class CustomFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributesForElementsInRect = super.layoutAttributesForElements(in: rect)
var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()
for attributes in attributesForElementsInRect! {
if !(attributes.representedElementKind == UICollectionElementKindSectionHeader
|| attributes.representedElementKind == UICollectionElementKindSectionFooter) {
// cells will be customise here, but Header and Footer will have layout without changes.
}
newAttributesForElementsInRect.append(attributes)
}
return newAttributesForElementsInRect
}
}
class YourViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let headerNib = UINib.init(nibName: "HeaderCell", bundle: nil)
collectionView.register(headerNib, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderCell")
let footerNib = UINib.init(nibName: "FooterCell", bundle: nil)
collectionView.register(footerNib, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: "FooterCell")
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderCell", for: indexPath) as! HeaderCell
return headerView
case UICollectionElementKindSectionFooter:
let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FooterCell", for: indexPath) as! FooterCell
return footerView
default:
return UICollectionReusableView()
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 45)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 25)
}
}
答案 2 :(得分:-2)
为什么要为ReuseIdentifier传递GridRulerView.Kind?
reusableHeaderView = collectionView.dequeueReusableSupplementaryViewOfKind(GridRulerView.Kind, withReuseIdentifier: **GridRulerView.Kind**, forIndexPath: indexPath) as! UICollectionReusableView
应如下所示。
var supplementView = collectionView.dequeueReusableSupplementaryViewOfKind(实物, withReuseIdentifier:Identifiers.HeaderIdentifier.rawValue, forIndexPath:indexPath)as UICollectionReusableView