Пользовательский UICollectionViewLayout, передающий атрибуты layout для несуществующего indexpath - PullRequest
0 голосов
/ 05 июня 2018

Я пытаюсь создать пользовательский UICollectionViewFlowLayout, в котором элементы могут отображаться либо вертикально, либо горизонтально (см. Ниже)

Вертикально:

| S | S | S |
|1 | 4 | 7 |
| 2 | 5 | 8 |
| 3 | 6 | 9 |

Горизонтально:

| S | 1 | 2 | 3 |
| S | 4 | 5 | 6 |
| S | 7 | 8 | 9 |

Тем не менее, я не могу заставить работать вертикаль.Горизонтальный макет работает просто отлично, но каждый раз, когда я хочу использовать вертикальный макет, он просто вылетает со следующей ошибкой:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x7ffcf597fa60> {length = 2, path = 1 - 6}'

Когда я использую те же данные и заставляю их использоватьгоризонтальный макет работает нормально, и это заставляет меня поверить, что я что-то не так с атрибутами макета.

Мой пользовательский класс макета потока:

import UIKit

class CustomCollectionViewLayout: UICollectionViewFlowLayout {

var horizontalInset = 0.0 as CGFloat
var verticalInset = 0.0 as CGFloat

var minimumItemWidth = 0.0 as CGFloat
var maximumItemWidth = 0.0 as CGFloat

var itemHeight = 0.0 as CGFloat
var itemWidth = 0.0 as CGFloat

var initialItemSizeSet: Bool = false

var _layoutAttributes = Dictionary<String, UICollectionViewLayoutAttributes>()
var _contentSize = CGSize.zero

var transposed: Bool

init(transposed: Bool) {
    self.transposed = transposed

    super.init()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func prepare() {
    super.prepare()

    _layoutAttributes = Dictionary<String, UICollectionViewLayoutAttributes>()
    _layoutAttributes.removeAll()

    let path = IndexPath(item: 0, section: 0)
    let attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: path)

    let headerHeight = CGFloat(self.itemHeight / 4)
    attributes.frame = CGRect(x: 0, y: 0, width: self.collectionView!.frame.size.width, height: headerHeight)

    let headerKey = layoutKeyForHeaderAtIndexPath(path)
    _layoutAttributes[headerKey] = attributes

    let numberOfSections = (self.collectionView!.numberOfSections)-1

    var yOffset = headerHeight
    var xOffset = self.horizontalInset

    var largestNumberOfItems = 0

    for section in 0...numberOfSections {
        let numberOfItems = self.collectionView!.numberOfItems(inSection: section)

        if(numberOfItems > largestNumberOfItems){
            largestNumberOfItems = numberOfItems
        }

        for item in 0...numberOfItems {
            let indexPath = IndexPath(item: item, section: section)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)

            var itemSize = CGSize.zero

            //Every cell has a fixed size
            if(!initialItemSizeSet){
                self.itemWidth = 75
                self.itemHeight = 75
                initialItemSizeSet = true
            }

            itemSize.width = itemWidth
            itemSize.height = itemHeight

            attributes.frame = CGRect(x: xOffset, y: yOffset, width: itemSize.width, height: itemSize.height).integral
            print("Created frame with xOffset \(xOffset), yOffset \(yOffset), width \(itemSize.width) and height \(itemSize.height)")
            let key = layoutKeyForIndexPath(indexPath)
            print("Attached indexPath key: \(key)")
            _layoutAttributes[key] = attributes

            if transposed {
                yOffset += itemSize.height
                yOffset += self.verticalInset
            }else {
                xOffset += itemSize.width
                xOffset += self.horizontalInset
            }

        }

        //After going through all items in section, check if current section is not the last section
        if !(section == numberOfSections) {
            if transposed {
                //Increase xOffset for correct horizontal placement
                xOffset += self.horizontalInset
                xOffset += self.itemWidth

                //Reset yOffset for correct spacing between cells
                yOffset = self.verticalInset
            }else {
                //Increase yOffset for correct vertical placement
                yOffset += self.verticalInset
                yOffset += self.itemHeight

                //Reset xOffset for correct spacing between cells
                xOffset = self.horizontalInset
            }
            print("Finished adding items in section \(section), xOffset: \(xOffset), yOffset: \(yOffset)")
        }else{
            if transposed {
                yOffset = self.verticalInset
            }else {
                xOffset = self.horizontalInset
            }
        }
    }

    if transposed {
        xOffset += self.itemWidth
    }else {
        yOffset += self.itemHeight
    }

    //Calculate size of content based on largest number of items in the sections
    print("xOffset: \(xOffset), yOffset: \(yOffset)")
    if transposed {
        _contentSize = CGSize(width: xOffset + self.horizontalInset, height: (CGFloat(largestNumberOfItems) * (itemHeight + yOffset)))
    }else {
        _contentSize = CGSize(width: (CGFloat(largestNumberOfItems) * (itemWidth + xOffset)), height: yOffset + self.verticalInset)
    }

    print("content size :\(_contentSize)")
}

func changeItemSize(_ newWidth: CGFloat, newHeight: CGFloat){
    self.itemHeight = newHeight
    self.itemWidth = newWidth
}

func changeInset(_ newHorizontalInset: CGFloat, newVerticalInset: CGFloat){
    self.horizontalInset = newHorizontalInset
    self.verticalInset = newVerticalInset
}

func layoutKeyForIndexPath(_ indexPath : IndexPath) -> String {
    return "\(indexPath.section)_\(indexPath.row)"
}

func layoutKeyForHeaderAtIndexPath(_ indexPath : IndexPath) -> String {
    return "s_\(indexPath.section)_\(indexPath.row)"
}

override var collectionViewContentSize : CGSize {
    return _contentSize
}

override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {

    let headerKey = layoutKeyForIndexPath(indexPath)
    return _layoutAttributes[headerKey]
}

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {

    let key = layoutKeyForIndexPath(indexPath)
    return _layoutAttributes[key]
}

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

    let predicate = NSPredicate {  [unowned self] (evaluatedObject, bindings) -> Bool in
        let layoutAttribute = self._layoutAttributes[evaluatedObject as! String]
        return rect.intersects(layoutAttribute!.frame)
    }

    let dict = _layoutAttributes as NSDictionary
    let keys = dict.allKeys as NSArray
    let matchingKeys = keys.filtered(using: predicate)

    return dict.objects(forKeys: matchingKeys, notFoundMarker: NSNull()) as? [UICollectionViewLayoutAttributes]
}
}

1 Ответ

0 голосов
/ 05 июня 2018

Таким образом, получается, что причиной сбоя было то, что я забыл, что количество элементов, возвращаемых представлением коллекции numberOfItems(inSection), не было нулевым.Я не думал смотреть туда, потому что горизонтальная компоновка исправила это несоответствие по неизвестной причине.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...