2017-08-23 3 views
1

Ich habe ein Problem während UICollectionView benutzerdefinierte Layout-Implementierung.UICollectionViewCell Höhe mit benutzerdefinierten Layout

Die Sache ist, dass ich Sammelansicht Zelle Höhe in benutzerdefinierten Layouts prepare() berechnen muss. Um dies zu tun, habe ich:

Methode in benutzerdefinierten Layout-Delegierten-Protokoll, das in meinem View-Controller implementiert ist.

Allerdings wird diese Methode aufgerufen, bevor die Zelle aus der Warteschlange genommen wird und tatsächlich irgendwelche Daten hat, auf deren Grundlage ich ihre Höhe berechnen könnte. Wenn also der Inhalt der Zelle die anfänglichen Grenzen überschreitet, kann ich keinen Teil des Inhalts sehen.

Hat jemand das gleiche Problem mit benutzerdefinierten Layouts festgestellt? Wie hast du es gelöst?

Protokoll für benutzerdefiniertes Layout:

protocol CustomLayoutDelegateProtocol: class { 
    func numberOfSectionsInRow() -> Int 
    func indecesOfSectionsInRow() -> [Int] 
    func minimumInteritemSpace() -> CGFloat 
    func heightForItem(_ collectionView: UICollectionView, at indexPath: IndexPath) -> CGFloat 
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets 
} 

kundenspezifische Layout selbst:

class CustomLayoutClass: UICollectionViewLayout { 

    weak var delegate: CustomLayoutDelegateProtocol? { 
     didSet { 
      setupLayout() 
     } 
    } 

    private var cache = [UICollectionViewLayoutAttributes]() 
    private var contentHeight: CGFloat = 0.0 
    private var contentWidth: CGFloat { 
     guard let collectionView = collectionView else { return 0 } 
     return collectionView.bounds.width 
    } 
    private var interitemSpace: CGFloat? 
    private var numberOfColumns: Int? 
    private var columnedSections: [Int]? 


    override init() { 
     super.init() 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

    private func setupLayout() { 
     numberOfColumns = delegate?.numberOfSectionsInRow() 
     columnedSections = delegate?.indecesOfSectionsInRow() 
     interitemSpace = delegate?.minimumInteritemSpace() 
    } 

    override func invalidateLayout() { 
     cache.removeAll() 
     super.invalidateLayout() 
    } 

    override func prepare() { 
     if cache.isEmpty { 
      guard let collectionView = collectionView, 
        let numberOfColumns = numberOfColumns, 
        let columnedSections = columnedSections, 
        let interitemSpace = interitemSpace else { return } 

      let columnWidth = (contentWidth/CGFloat(numberOfColumns)) 
      var xOffset = [CGFloat]() 
      for column in 0..<numberOfColumns { 
       var interitemSpace = interitemSpace 
       if column == 0 { interitemSpace = 0 } 
       xOffset.append(CGFloat(column) * columnWidth + interitemSpace) 
      } 

      var yOffset: CGFloat = 0.0 

      for section in 0..<collectionView.numberOfSections { 
       for item in 0..<collectionView.numberOfItems(inSection: section) { 
        let indexPath = IndexPath(item: item, section: section) 
        guard let sectionInsets = delegate?.collectionView(collectionView, layout: self, insetForSectionAt: indexPath.section), 
          let height = delegate?.heightForItem(collectionView, at: indexPath) else { continue } 
        let width = columnedSections.contains(section) ? columnWidth : contentWidth 

        let xOffsetIdx = columnedSections.contains(section) ? columnedSections.index(of: section)! % numberOfColumns : 0 

        yOffset += sectionInsets.top 
        let frame = CGRect(x: xOffset[xOffsetIdx], y: yOffset, width: width, height: height) 

        let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) 
        attributes.frame = frame 
        cache.append(attributes) 

        contentHeight = max(contentHeight, frame.maxY) 

        let isLastInRow = (columnedSections.contains(section) && columnedSections.index(of: section)! % numberOfColumns == (numberOfColumns-1)) 
        let isNotColumnedSection = !columnedSections.contains(section) 

        if isLastInRow || isNotColumnedSection { 
         yOffset += height + sectionInsets.bottom 
        } 
       } 
      } 
     } 
    } 

    override var collectionViewContentSize: CGSize { 
     return CGSize(width: contentWidth, height: contentHeight) 
    } 

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

     for attributes in cache { 
      if attributes.frame.intersects(rect) { 
       layoutAttributes.append(attributes) 
      } 
     } 
     return layoutAttributes 
    } 
} 

und Implementierung für Protokoll (von View-Controller):

extension ViewController: CustomLayoutDelegateProtocol { 
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { 
    guard let sectionType = InvoiceSectionIndexType(rawValue: section) else { return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) } 

    switch sectionType { 
    case .receiver: 
     return UIEdgeInsets(top: Constants.bigLineSpace, left: 0, bottom: Constants.bigLineSpace, right: 0) 

    default: 
     return UIEdgeInsets(top: 0, left: 0, bottom: Constants.commonLineSpace, right: 0) 
    } 
} 

func numberOfSectionsInRow() -> Int { 
    return Constants.numberOfSectionsInRow 
} 

func indecesOfSectionsInRow() -> [Int] { 
    return [0, 1] 
} 

func minimumInteritemSpace() -> CGFloat { 
    return Constants.interitemSpace 
} 

func heightForItem(_ collectionView: UICollectionView, at indexPath: IndexPath) -> CGFloat { 
    guard let sectionType = InvoiceSectionIndexType(rawValue: indexPath.section) else { return 0 } 

    switch sectionType { 
    case .next: 
     return Constants.nextButtonHeight 

    default: 
     return Constants.estimatedRowHeight 
    } 
} 
} 
+0

zeigen Sie einen Code, so dass wir Ihnen besser – 3stud1ant3

+0

@ user1000 helfen hinzugefügt Protokoll, Layout und Protokoll-Implementierung in Frage –

+0

Haben Sie versucht UICollectionViewDelegateFlowLayout Protokoll Objektgröße für die Berechnung? – KrishnaKumar

Antwort

0

beschloss ich dieses Problem auf meinem eigene und die Lösung ist ziemlich einfach, wenn auch nicht sehr offensichtlich.

Da wir indexPath für Betonzelle haben, können wir unseren Inhalt finden (in meinem Fall ist Inhalt ein einfacher Text, der in ein Etikett eingefügt wird). Dann können wir eine Beschriftung erstellen (die wir nicht anzeigen, da sie nur für Berechnungen benötigt wird) mit Breite (in meinem Fall kenne ich Breite für Zellen) und Höhe, die CGFloat.greatestFiniteMagnitude ist. Wir tragen dann sizeToFit() auf unser Label und jetzt haben wir Label mit entsprechender Höhe. Das einzige, was wir jetzt tun sollten - diese neue Höhe auf unsere Zelle anwenden.

Beispielcode:

func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat { 
     // ask your DataSource for piece of data with indexPath 

     let testLabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)) 
     testLabel.text = // your text 
     testLabel.sizeToFit() 

     if testLabel.bounds.height > Constants.defaultContentLabelHeight { 
      return Constants.estimatedRowHeight + (testLabel.bounds.height - Constants.defaultContentLabelHeight) 
     } else { 
      return Constants.estimatedRowHeight 
     } 
    } 
Verwandte Themen