Пользовательский макет UICollectionview: в некоторых индексах больше видимых ячеек, чем в других? - PullRequest
0 голосов
/ 10 октября 2018

Я сталкиваюсь со странной проблемой, которую, похоже, не могу понять или найти в Интернете.

Поэтому я пытаюсь повторить пользовательский интерфейс Shazam Discover с UICollectionView и пользовательскимUICollectionViewFlowlayout.

Пока все работает довольно хорошо, но при добавлении эффекта «стека карт», который я (или, скорее, человек, который его реализовывал) заметил, что возникает странная проблема, когдав некоторых случаях (точнее, когда видны определенные индексы, в примере это строки 5, 9) вместо 3 будет 4 видимых ячейки. Я полагаю, что это как-то связано с повторным использованием ячеек, ноЯ не уверен, почему он это делает.Я посмотрел на размеры отдельных ячеек, и все они кажутся одинаковыми, поэтому дело не в том, что ячейки имеют разные размеры.

Кто-нибудь имеет представление о том, почему это может происходить?Любая помощь или предложения действительно приветствуются.

Я добавлю фрагмент кода пользовательского потока и скриншоты ниже.Вы можете скачать полный проект здесь , или, альтернативно, проверить PR на Github .

Вот визуальное сравнение:

enter image description here enter image description here

Исходный код пользовательского потока:

import UIKit

/// Custom `UICollectionViewFlowLayout` that provides the flowlayout information like paging and `CardCell` movements.
internal class VerticalCardSwiperFlowLayout: UICollectionViewFlowLayout {

    /// This property sets the amount of scaling for the first item.
    internal var firstItemTransform: CGFloat?
    /// This property enables paging per card. The default value is true.
    internal var isPagingEnabled: Bool = true
    /// Stores the height of a CardCell.
    internal var cellHeight: CGFloat!

    internal override func prepare() {
        super.prepare()

        assert(collectionView!.numberOfSections == 1, "Number of sections should always be 1.")
        assert(collectionView!.isPagingEnabled == false, "Paging on the collectionview itself should never be enabled. To enable cell paging, use the isPagingEnabled property of the VerticalCardSwiperFlowLayout instead.")
    }

    internal override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let items = NSMutableArray (array: super.layoutAttributesForElements(in: rect)!, copyItems: true)

        items.enumerateObjects(using: { (object, index, stop) -> Void in
            let attributes = object as! UICollectionViewLayoutAttributes

            self.updateCellAttributes(attributes)
        })

        return items as? [UICollectionViewLayoutAttributes]
    }

    // We invalidate the layout when a "bounds change" happens, for example when we scale the top cell. This forces a layout update on the flowlayout.
    internal override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }

    // Cell paging
    internal override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

        // If the property `isPagingEnabled` is set to false, we don't enable paging and thus return the current contentoffset.
        guard isPagingEnabled else {
            let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
            return latestOffset
        }

        // Page height used for estimating and calculating paging.
        let pageHeight = cellHeight + self.minimumLineSpacing

        // Make an estimation of the current page position.
        let approximatePage = self.collectionView!.contentOffset.y/pageHeight

        // Determine the current page based on velocity.
        let currentPage = (velocity.y < 0.0) ? floor(approximatePage) : ceil(approximatePage)

        // Create custom flickVelocity.
        let flickVelocity = velocity.y * 0.4

        // Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
        let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)

        // Calculate newVerticalOffset.
        let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) - self.collectionView!.contentInset.top

        return CGPoint(x: proposedContentOffset.x, y: newVerticalOffset)
    }

    internal override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {

        // make sure the zIndex of the next card is higher than the one we're swiping away.
        let nextIndexPath = IndexPath(row: itemIndexPath.row + 1, section: itemIndexPath.section)
        let nextAttr = self.layoutAttributesForItem(at: nextIndexPath)
        nextAttr?.zIndex = nextIndexPath.row

        // attributes for swiping card away
        let attr = self.layoutAttributesForItem(at: itemIndexPath)

        return attr
    }

    /**
     Updates the attributes.
     Here manipulate the zIndex of the cards here, calculate the positions and do the animations.
     - parameter attributes: The attributes we're updating.
     */
    fileprivate func updateCellAttributes(_ attributes: UICollectionViewLayoutAttributes) {
        let minY = collectionView!.bounds.minY + collectionView!.contentInset.top
        let maxY = attributes.frame.origin.y

        let finalY = max(minY, maxY)
        var origin = attributes.frame.origin
        let deltaY = (finalY - origin.y) / attributes.frame.height
        let translationScale = CGFloat((attributes.zIndex + 1) * 10)

        // create stacked effect (cards visible at bottom
        if let itemTransform = firstItemTransform {
            let scale = 1 - deltaY * itemTransform
            var t = CGAffineTransform.identity
            t = t.scaledBy(x: scale, y: 1)
            t = t.translatedBy(x: 0, y: (translationScale + deltaY * translationScale))

            attributes.transform = t
        }

        origin.x = (self.collectionView?.frame.width)! / 2 - attributes.frame.width / 2 - (self.collectionView?.contentInset.left)!
        origin.y = finalY
        attributes.frame = CGRect(origin: origin, size: attributes.frame.size)
        attributes.zIndex = attributes.indexPath.row
    }
}

edit 1: Просто как дополнительныйПояснение: конечный конечный результат должен выглядеть примерно так:

enter image description here

edit 2: Кажется, что происходит каждые 4-5 карт вы прокручиваете из моего тестирования.

Ответы [ 2 ]

0 голосов
/ 18 октября 2018

Ошибка в том, как вы определили frame.origin.y для каждого атрибута.Точнее, значение, которое вы держите в minY и определяет, сколько ячеек вы держите на экране.(Я отредактирую этот ответ и объясню подробнее позже, а пока попробуйте заменить следующий код)

var minY = collectionView!.bounds.minY + collectionView!.contentInset.top
let maxY = attributes.frame.origin.y

if minY > attributes.frame.origin.y + attributes.bounds.height + minimumLineSpacing + collectionView!.contentInset.top {
   minY = 0
}
0 голосов
/ 10 октября 2018

У вас есть макет, который наследуется от макета потока.Вы переопределили layoutAttributesForElements(in rect:), где вы берете все элементы из super.layoutAttributesForElements, а затем для каждого изменяете свойства в методе updateCellAttributes.

. Как правило, это хороший способ сделать подкласс макета потока.,UICollectionViewFlowLayout выполняет большую часть тяжелой работы - выясняет, где должен быть каждый элемент, какие элементы находятся в прямоугольнике, каковы их основные атрибуты, как они должны быть дополнены и т. Д., И вы можете просто изменить несколько свойств после«тяжелая» работа выполнена.Это прекрасно работает, когда вы добавляете вращение или непрозрачность или какую-либо другую функцию, которая не меняет местоположение элемента.

Вы попадаете в неприятности, когда меняете кадр предметов с помощью updateCellAttributes.Тогда у вас может возникнуть ситуация, когда у вас будет ячейка, которая вообще не появилась бы в кадре для обычного макета потока, но теперь ДОЛЖНА появиться из-за вашей модификации.Таким образом, атрибут super.layoutAttributesForElements (in rect: CGRect) не возвращается вообще, поэтому они вообще не отображаются.Вы также можете столкнуться с противоположной проблемой, заключающейся в том, что ячейки, которые вообще не должны быть в кадре, находятся в представлении, но преобразованы так, что пользователь не может их видеть.

Вы недостаточно объяснили, какой эффектвы пытаетесь это сделать, и почему вы считаете, что наследование от UIFlowLayout правильно, чтобы я мог вам конкретно помочь.Но я надеюсь, что предоставил вам достаточно информации, чтобы вы могли найти проблему самостоятельно.

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