2013-05-14 20 views
37

我有隨機細胞UICollectionView。有什麼方法可以讓我排列中心嗎?如何將行集中在UICollectionView中?

這是它的外觀默認:

[ x x x x x x ] 
[ x x x x x x ] 
[ x x   ] 

這裏所需的佈局是什麼樣子:

[ x x x x x ] 
[ x x x x x ] 
[ x x ] 

回答

23

一點背景知識第一 - 一個UICollectionViewUICollectionViewLayout這就決定合併細胞如何置於視野中。這意味着集合視圖非常靈活(您可以使用它創建幾乎任何佈局),但也意味着修改佈局可能會有點混亂。

創建一個全新的佈局類很複雜,因此您想要嘗試修改默認佈局(UICollectionViewFlowLayout)以獲得中心對齊。爲了使它更簡單,你可能想要避免對流佈局本身進行子類化。

這裏有一個方法(它可能不是最好的方法,但它是第一個我能想到的) - 你的細胞分裂成兩個部分,具體如下:

[ x x x x x ] <-- Section 1 
[ x x x x x ] <-- Section 1 
[ x x ] <-- Section 2 

這應該是相當簡單的,前提是您知道滾動視圖的寬度以及每行可放入的單元格數量。

然後,使用collectionView:layout:insetForSectionAtIndex:委託方法來設定邊距爲您的第二部分,以便它似乎是垂直居中。完成此操作後,只需確保重新計算相應的部門/插槽,以便支持縱向和橫向方向。

這裏有一個類似的問題 - How to center align the cells of a UICollectionView? - 它會進入更多關於插入方法的細節,儘管它並不完全像你一樣做同樣的事情。

+0

嗯,我不知道這是做到這一點的最好方法,但它的工作原理。而這一切我需要。謝謝。 – deycall 2013-05-14 14:01:28

+0

我認爲這可能是最簡單的,根據行數/不分類佈局。如果你最終做了更多的佈局工作,你可能需要考慮這種方法。 – lxt 2013-05-14 14:03:25

+0

您也可以繼承'UICollectionViewFlowLayout'而不是'UICollectionViewLayout'。佈局仍然是基於流程的一個小變化,這樣做可以完成大部分繁重的工作,您只需自定義您想要的位。 – Fogmeister 2013-05-14 14:06:42

44

我不得不做這樣的事情,但所需的所有細胞的一個部分。將UICollectionViewFlowLayout擴展到中心單元非常簡單。我做了一個吊艙:

https://github.com/keighl/KTCenterFlowLayout

enter image description here

+1

爲了讓KTCenterFlowLayout正常工作,我不得不明確地設置代碼中的單元格大小,即使它已經通過故事板設置: 'let layout = KTCenterFlowLayout(); layout.itemSize = CGSizeMake(85,85)' – David 2014-12-28 18:02:56

+0

這件事很酷!謝謝! – Andy 2015-02-26 02:32:31

+1

您也可以將其設置爲界面構建器中您的集合視圖的佈局。 謝謝凱爾,這太棒了! – 2015-10-19 09:10:02

2

如果任何人有有2列的CollectionView,如果項目的數量爲奇數,則最後一項應該居中對齊。然後用這個

DNLastItemCenteredLayout

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 
    NSArray *attributes = [super layoutAttributesForElementsInRect:rect]; 

    for (UICollectionViewLayoutAttributes *attribute in attributes) { 
     NSInteger itemCount = [self.collectionView.dataSource collectionView:self.collectionView 
                 numberOfItemsInSection:attribute.indexPath.section]; 
     if (itemCount % 2 == 1 && attribute.indexPath.item == itemCount - 1) { 
      CGRect originalFrame = attribute.frame; 
      attribute.frame = CGRectMake(self.collectionView.bounds.size.width/2-originalFrame.size.width/2, 
             originalFrame.origin.y, 
             originalFrame.size.width, 
             originalFrame.size.height); 
     } 
    } 

    return attributes; 
} 
1

只是鏈接這個流佈局。你可以對齊中心,左邊,右邊。

// 
    // CellAllignmentFlowLayout.swift 
    // UICollectionView 
    // 
    // Created by rajeshkumar Lingavel on 8/11/15. 
    // Copyright © 2015 rajeshkumar Lingavel. All rights reserved. 
    // 

import UIKit 
enum SZAlignment:Int { 
    case Center, 
     left, 
     Right 
} 
class CellAllignmentFlowLayout: UICollectionViewFlowLayout { 
    var alignment:SZAlignment! 
    var padding:CGFloat! 
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 
//  NSArray *allAttributesInRect = [super 
//   layoutAttributesForElementsInRect:rect]; 

     let allAttributesInRect:NSArray = super.layoutAttributesForElementsInRect(rect)! 
     var changedAttributes:NSArray = NSArray() 
     switch(alignment.rawValue){ 
      case 0: 
       changedAttributes = alignCenter(allAttributesInRect) 
      case 1: 
       changedAttributes = alignLeft(allAttributesInRect) 
      case 2: 
       changedAttributes = alignRight(allAttributesInRect) 
      default: 
       assertionFailure("No Direction") 
     } 

     return changedAttributes as? [UICollectionViewLayoutAttributes] 
    } 


    private func alignCenter(allAttributesInRect:NSArray) -> NSArray{ 


     let numberOfSection:Int = (self.collectionView?.numberOfSections())! 

     let redefiendArray = NSMutableArray() 

     for i in 0 ..< numberOfSection { 

       let thisSectionObjects = sectionObjects(allAttributesInRect, section: i) 
       let totalLines = numberOfLines(thisSectionObjects, section: i) 
       let lastrowObjects = lastRow(thisSectionObjects, numberOfRows: totalLines, section: i) 
       let lastRowObjectsRow = setMiddleTheLastRow(lastrowObjects) 
       let start = (thisSectionObjects.count - lastrowObjects.count) 

       for j in start..<thisSectionObjects.count{ 
        thisSectionObjects.replaceObjectAtIndex(j, withObject: lastRowObjectsRow.objectAtIndex(j - start)) 
       } 
      redefiendArray.addObjectsFromArray(thisSectionObjects as [AnyObject]) 
     } 




     return redefiendArray 
    } 
    private func alignLeft(allAttributesInRect:NSArray) -> NSArray{ 


     return allAttributesInRect; 

    } 
    private func alignRight(allAttributesInRect:NSArray) -> NSArray{ 
     return allAttributesInRect; 

    } 

    private func getTotalLenthOftheSection(section:Int,allAttributesInRect:NSArray) -> CGFloat{ 

     var totalLength:CGFloat = 0.0 
     totalLength = totalLength + (CGFloat (((self.collectionView?.numberOfItemsInSection(section))! - 1)) * padding) 
     for attributes in allAttributesInRect { 

      if(attributes.indexPath.section == section){ 
       totalLength = totalLength + attributes.frame.width 
      } 
     } 

     return totalLength 
    } 

    private func numberOfLines(allAttributesInRect:NSArray,section:Int)-> Int{ 
     var totalLines:Int = 0 
     for attributes in allAttributesInRect { 
      if(attributes.indexPath.section == section){ 
       if (attributes.frame.origin.x == self.sectionInset.left){ 
        totalLines = totalLines + 1 
       } 
      } 
     } 
     return totalLines 
    } 
    private func sectionObjects(allAttributesInRect:NSArray,section:Int) -> NSMutableArray{ 
     let objects:NSMutableArray = NSMutableArray() 
     for attributes in allAttributesInRect { 
      if(attributes.indexPath.section == section){ 
       objects.addObject(attributes) 
      } 
     } 
     return objects 
    } 

    private func lastRow(allAttributesInRect:NSArray,numberOfRows:Int,section:Int) -> NSMutableArray{ 
     var totalLines:Int = 0 
     let lastRowArrays:NSMutableArray = NSMutableArray() 
     for attributes in allAttributesInRect { 
      if(attributes.indexPath.section == section){ 
       if (attributes.frame.origin.x == self.sectionInset.left){ 
        totalLines = totalLines + 1 
        if(totalLines == numberOfRows){ 
         lastRowArrays.addObject(attributes) 
        } 
       } 
       else{ 
        if(totalLines == numberOfRows){ 
         lastRowArrays.addObject(attributes) 
        } 
       } 
      } 
     } 
     return lastRowArrays 
    } 
    private func setMiddleTheLastRow(lastRowAttrs:NSMutableArray)->NSMutableArray{ 
     let redefinedValues = NSMutableArray() 
     let totalLengthOftheView = self.collectionView?.frame.width 
     var totalLenthOftheCells:CGFloat = 0.0 
     totalLenthOftheCells = totalLenthOftheCells + (CGFloat (lastRowAttrs.count) - 1) * padding 

     for attrs in lastRowAttrs{ 
      totalLenthOftheCells = totalLenthOftheCells + attrs.frame.width 
     } 

     var initalValue = (totalLengthOftheView!/2) - (totalLenthOftheCells/2) 

     for i in 0..<lastRowAttrs.count { 
      let changeingAttribute:UICollectionViewLayoutAttributes = lastRowAttrs[i] as! UICollectionViewLayoutAttributes 
      var frame = changeingAttribute.frame 
      frame.origin.x = initalValue 
      changeingAttribute.frame = frame 
      redefinedValues.addObject(changeingAttribute) 
      initalValue = initalValue + changeingAttribute.frame.width + padding 
     } 

     return redefinedValues; 
    } 


} 
2

這可以用(相對)簡單的自定義佈局,從UICollectionViewFlowLayout子類來實現。下面是Swift一個例子:

/** 
* A simple `UICollectionViewFlowLayout` subclass that would make sure the items are center-aligned in the collection view, when scrolling vertically. 
*/ 
class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout { 

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 
     guard let suggestedAttributes = super.layoutAttributesForElementsInRect(rect) else { return nil } 

     guard scrollDirection == .Vertical else { return suggestedAttributes } 

     var newAttributes: [UICollectionViewLayoutAttributes] = [] 

     /// We will collect items for each row in this array 
     var currentRowAttributes: [UICollectionViewLayoutAttributes] = [] 
     /// We will use this variable to detect new rows when iterating over items 
     var yOffset:CGFloat = sectionInset.top 
     for attributes in suggestedAttributes { 
      /// If we happen to run into a new row... 
      if attributes.frame.origin.y != yOffset { 
       /* 
       * Update layout of all items in the previous row and add them to the resulting array 
       */ 
       centerSingleRowWithItemsAttributes(&currentRowAttributes, rect: rect) 
       newAttributes += currentRowAttributes 
       /* 
       * Reset the accumulated values for the new row 
       */ 
       currentRowAttributes = [] 
       yOffset = attributes.frame.origin.y 
      } 
      currentRowAttributes += [attributes] 
     } 
     /* 
     * Update the layout of the last row. 
     */ 
     centerSingleRowWithItemsAttributes(&currentRowAttributes, rect: rect) 
     newAttributes += currentRowAttributes 

     return newAttributes 
    } 

    /** 
    Updates the attributes for items, so that they are center-aligned in the given rect. 

    - parameter attributes: Attributes of the items 
    - parameter rect:  Bounding rect 
    */ 
    private func centerSingleRowWithItemsAttributes(inout attributes: [UICollectionViewLayoutAttributes], rect: CGRect) { 
     guard let item = attributes.last else { return } 

     let itemsCount = CGFloat(attributes.count) 
     let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1)) 
     var leftOffset = sideInsets/2 

     for attribute in attributes { 
      attribute.frame.origin.x = leftOffset 
      leftOffset += attribute.frame.width + minimumInteritemSpacing 
     } 
    } 
} 
2

我子類UICollectionViewFlowLayout - 改變了代碼,我已經找到了here左對齊集合視圖。

  1. 左對齊的集合視圖
  2. 組的屬性爲線陣列
  3. 對於每個行:
    • 計算右側
    • 空間添加的空間的一半爲每個該行的屬性

它看起來是這樣的:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 

    NSArray *attributesForElementsInRect = [super layoutAttributesForElementsInRect:rect]; 
    NSMutableArray *newAttributesForElementsInRect = [[NSMutableArray alloc] initWithCapacity:attributesForElementsInRect.count]; 

    CGFloat leftMargin = self.sectionInset.left; 
    NSMutableArray *lines = [NSMutableArray array]; 
    NSMutableArray *currLine = [NSMutableArray array]; 

    for (UICollectionViewLayoutAttributes *attributes in attributesForElementsInRect) { 
     // Handle new line 
     BOOL newLine = attributes.frame.origin.x <= leftMargin; 
     if (newLine) { 
      leftMargin = self.sectionInset.left; //will add outside loop 
      currLine = [NSMutableArray arrayWithObject:attributes]; 
     } else { 
      [currLine addObject:attributes]; 
     } 

     if ([lines indexOfObject:currLine] == NSNotFound) { 
      [lines addObject:currLine]; 
     } 

     // Align to the left 
     CGRect newLeftAlignedFrame = attributes.frame; 
     newLeftAlignedFrame.origin.x = leftMargin; 
     attributes.frame = newLeftAlignedFrame; 

     leftMargin += attributes.frame.size.width + self.minimumInteritemSpacing; 
     [newAttributesForElementsInRect addObject:attributes]; 
    } 

    // Center left aligned lines 
    for (NSArray *line in lines) { 
     UICollectionViewLayoutAttributes *lastAttributes = line.lastObject; 
     CGFloat space = CGRectGetWidth(self.collectionView.frame) - CGRectGetMaxX(lastAttributes.frame); 

     for (UICollectionViewLayoutAttributes *attributes in line) { 
      CGRect newFrame = attributes.frame; 
      newFrame.origin.x = newFrame.origin.x + space/2; 
      attributes.frame = newFrame; 

     } 
    } 

    return newAttributesForElementsInRect; 
} 

希望它可以幫助別人:)

1

雨燕3.0的類版本:

class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout { 
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 
     guard let suggestedAttributes = super.layoutAttributesForElements(in: rect) else { return nil } 

     guard scrollDirection == .vertical else { return suggestedAttributes } 

     var newAttributes: [UICollectionViewLayoutAttributes] = [] 

     var currentRowAttributes: [UICollectionViewLayoutAttributes] = [] 
     var yOffset:CGFloat = sectionInset.top 
     for attributes in suggestedAttributes { 
      if attributes.frame.origin.y != yOffset { 
       centerSingleRowWithItemsAttributes(attributes: &currentRowAttributes, rect: rect) 
       newAttributes += currentRowAttributes 
       currentRowAttributes = [] 
       yOffset = attributes.frame.origin.y 
      } 
      currentRowAttributes += [attributes] 
     } 
     centerSingleRowWithItemsAttributes(attributes: &currentRowAttributes, rect: rect) 
     newAttributes += currentRowAttributes 

     return newAttributes 
    } 


    private func centerSingleRowWithItemsAttributes(attributes: inout [UICollectionViewLayoutAttributes], rect: CGRect) { 
     guard let item = attributes.last else { return } 

     let itemsCount = CGFloat(attributes.count) 
     let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1)) 
     var leftOffset = sideInsets/2 

     for attribute in attributes { 
      attribute.frame.origin.x = leftOffset 
      leftOffset += attribute.frame.width + minimumInteritemSpacing 
     } 
    } 
} 
+0

嗨,我如何使用這個類。即時通訊新的xcode。 – MRizwan33 2017-10-03 07:17:06

0

我做了凱爾·特拉斯科特的斯威夫特4版本answer

import UIKit 

class CenterFlowLayout: UICollectionViewFlowLayout { 

    private var attrCache = [IndexPath: UICollectionViewLayoutAttributes]() 

    override func prepare() { 
     attrCache = [:] 
    } 

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

     let sections = self.collectionView?.numberOfSections ?? 0 
     var section = 0 
     while section < sections { 
      let items = self.collectionView?.numberOfItems(inSection: section) ?? 0 
      var item = 0 
      while item < items { 
       let indexPath = IndexPath(row: item, section: section) 

       if let attributes = layoutAttributesForItem(at: indexPath), attributes.frame.intersects(rect) { 
        updatedAttributes.append(attributes) 
       } 

       let headerKind = UICollectionElementKindSectionHeader 
       if let headerAttributes = layoutAttributesForSupplementaryView(ofKind: headerKind, at: indexPath) { 
        updatedAttributes.append(headerAttributes) 
       } 

       let footerKind = UICollectionElementKindSectionFooter 
       if let footerAttributes = layoutAttributesForSupplementaryView(ofKind: footerKind, at: indexPath) { 
        updatedAttributes.append(footerAttributes) 
       } 

       item += 1 
      } 

      section += 1 
     } 

     return updatedAttributes 
    } 

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 
     if let attributes = attrCache[indexPath] { 
      return attributes 
     } 

     // Find the other items in the same "row" 
     var rowBuddies = [UICollectionViewLayoutAttributes]() 

     // Calculate the available width to center stuff within 
     // sectionInset is NOT applicable here because a) we're centering stuff 
     // and b) Flow layout has arranged the cells to respect the inset. We're 
     // just hijacking the X position. 
     var collectionViewWidth: CGFloat = 0 
     if let collectionView = collectionView { 
      collectionViewWidth = collectionView.bounds.width - collectionView.contentInset.left 
        - collectionView.contentInset.right 
     } 

     // To find other items in the "row", we need a rect to check intersects against. 
     // Take the item attributes frame (from vanilla flow layout), and stretch it out 
     var rowTestFrame: CGRect = super.layoutAttributesForItem(at: indexPath)?.frame ?? .zero 
     rowTestFrame.origin.x = 0 
     rowTestFrame.size.width = collectionViewWidth 

     let totalRows = self.collectionView?.numberOfItems(inSection: indexPath.section) ?? 0 

     // From this item, work backwards to find the first item in the row 
     // Decrement the row index until a) we get to 0, b) we reach a previous row 
     var rowStartIDX = indexPath.row 
     while true { 
      let prevIDX = rowStartIDX - 1 

      if prevIDX < 0 { 
       break 
      } 

      let prevPath = IndexPath(row: prevIDX, section: indexPath.section) 
      let prevFrame: CGRect = super.layoutAttributesForItem(at: prevPath)?.frame ?? .zero 

      // If the item intersects the test frame, it's in the same row 
      if prevFrame.intersects(rowTestFrame) { 
       rowStartIDX = prevIDX 
      } else { 
       // Found previous row, escape! 
       break 
      } 
     } 

     // Now, work back UP to find the last item in the row 
     // For each item in the row, add it's attributes to rowBuddies 
     var buddyIDX = rowStartIDX 
     while true { 
      if buddyIDX > totalRows - 1 { 
       break 
      } 

      let buddyPath = IndexPath(row: buddyIDX, section: indexPath.section) 

      if let buddyAttributes = super.layoutAttributesForItem(at: buddyPath), 
       buddyAttributes.frame.intersects(rowTestFrame), 
       let buddyAttributesCopy = buddyAttributes.copy() as? UICollectionViewLayoutAttributes { 
       // If the item intersects the test frame, it's in the same row 
       rowBuddies.append(buddyAttributesCopy) 
       buddyIDX += 1 
      } else { 
       // Encountered next row 
       break 
      } 
     } 

     let flowDelegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout 
     let selector = #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:)) 
     let delegateSupportsInteritemSpacing = flowDelegate?.responds(to: selector) ?? false 

     // x-x-x-x ... sum up the interim space 
     var interitemSpacing = minimumInteritemSpacing 

     // Check for minimumInteritemSpacingForSectionAtIndex support 
     if let collectionView = collectionView, delegateSupportsInteritemSpacing && rowBuddies.count > 0 { 
      interitemSpacing = flowDelegate?.collectionView?(collectionView, 
        layout: self, 
        minimumInteritemSpacingForSectionAt: indexPath.section) ?? 0 
     } 

     let aggregateInteritemSpacing = interitemSpacing * CGFloat(rowBuddies.count - 1) 

     // Sum the width of all elements in the row 
     var aggregateItemWidths: CGFloat = 0 
     for itemAttributes in rowBuddies { 
      aggregateItemWidths += itemAttributes.frame.width 
     } 

     // Build an alignment rect 
     // | |x-x-x-x| | 
     let alignmentWidth = aggregateItemWidths + aggregateInteritemSpacing 
     let alignmentXOffset: CGFloat = (collectionViewWidth - alignmentWidth)/2 

     // Adjust each item's position to be centered 
     var previousFrame: CGRect = .zero 
     for itemAttributes in rowBuddies { 
      var itemFrame = itemAttributes.frame 

      if previousFrame.equalTo(.zero) { 
       itemFrame.origin.x = alignmentXOffset 
      } else { 
       itemFrame.origin.x = previousFrame.maxX + interitemSpacing 
      } 

      itemAttributes.frame = itemFrame 
      previousFrame = itemFrame 

      // Finally, add it to the cache 
      attrCache[itemAttributes.indexPath] = itemAttributes 
     } 

     return attrCache[indexPath] 
    } 
} 
相關問題