【问题标题】:How can I center rows in UICollectionView?如何在 UICollectionView 中居中行?
【发布时间】:2013-05-14 13:12:56
【问题描述】:

我有一个带有随机单元格的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    ]  

【问题讨论】:

    标签: ios objective-c uicollectionview


    【解决方案1】:

    我不得不做这样的事情,但需要一个部分中的所有单元格。将UICollectionViewFlowLayout 扩展到中心单元非常简单。我做了一个豆荚:

    https://github.com/keighl/KTCenterFlowLayout

    【讨论】:

    • 要让 KTCenterFlowLayout 工作,我必须在代码中明确设置单元格大小,即使它已经通过情节提要设置:let layout = KTCenterFlowLayout(); layout.itemSize = CGSizeMake(85, 85)
    • 您也可以在界面生成器中将其设置为集合视图的布局。谢谢凯尔,这太棒了!
    • 非常感谢,凯尔!为我节省了几个小时!
    • 我的收藏视图想要完全相同的东西。如何使用 Objective-C 使用此布局?
    • 我制作了它的 Swift 4 版本:stackoverflow.com/a/47815971/4386668。谢谢@Kyle Truscott!
    【解决方案2】:

    先介绍一下背景 - 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? - 详细介绍了插入方法,尽管它并没有完全尝试做和你一样的事情。

    【讨论】:

    • 好吧,我不知道这是不是最好的方法,但它确实有效。这就是我所需要的。谢谢。
    • 我认为它可能是最简单的,就行数/不子类化布局而言。如果您最终要做更多的布局工作,您可能需要考虑这种方法。
    • 你也可以继承UICollectionViewFlowLayout而不是UICollectionViewLayout。布局仍然是基于流的,只是进行了微小的更改,这样做可以为您完成大部分繁重的工作,您只需自定义所需的部分即可。
    • 如果我有不同宽度的单元格,这种方法似乎使它更加复杂。您对我如何使用具有不同宽度的单元格来实现这一点有任何建议吗?谢谢
    【解决方案3】:

    在 Swift 4.1 和 iOS 11 中,根据您的需要,您可以选择以下两个完整实现中的一个来解决您的问题。


    #1。以固定大小居中UICollectionViewCells

    下面的实现展示了如何使用UICollectionViewLayoutlayoutAttributesForElements(in:)UICollectionViewFlowLayoutitemSize 来使UICollectionView 的单元格居中:

    CollectionViewController.swift

    import UIKit
    
    class CollectionViewController: UICollectionViewController {
    
        let columnLayout = FlowLayout(
            itemSize: CGSize(width: 140, height: 140),
            minimumInteritemSpacing: 10,
            minimumLineSpacing: 10,
            sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
        )
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            title = "Center cells"
    
            collectionView?.collectionViewLayout = columnLayout
            collectionView?.contentInsetAdjustmentBehavior = .always
            collectionView?.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
        }
    
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 7
        }
    
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
            return cell
        }
    
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            collectionView?.collectionViewLayout.invalidateLayout()
            super.viewWillTransition(to: size, with: coordinator)
        }
    
    }
    

    FlowLayout.swift

    import UIKit
    
    class FlowLayout: UICollectionViewFlowLayout {
    
        required init(itemSize: CGSize, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
            super.init()
    
            self.itemSize = itemSize
            self.minimumInteritemSpacing = minimumInteritemSpacing
            self.minimumLineSpacing = minimumLineSpacing
            self.sectionInset = sectionInset
            sectionInsetReference = .fromSafeArea
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
            guard scrollDirection == .vertical else { return layoutAttributes }
    
            // Filter attributes to compute only cell attributes
            let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })
    
            // Group cell attributes by row (cells with same vertical center) and loop on those groups
            for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
                // Get the total width of the cells on the same row
                let cellsTotalWidth = attributes.reduce(CGFloat(0)) { (partialWidth, attribute) -> CGFloat in
                    partialWidth + attribute.size.width
                }
    
                // Calculate the initial left inset
                let totalInset = collectionView!.safeAreaLayoutGuide.layoutFrame.width - cellsTotalWidth - sectionInset.left - sectionInset.right - minimumInteritemSpacing * CGFloat(attributes.count - 1)
                var leftInset = (totalInset / 2 * 10).rounded(.down) / 10 + sectionInset.left
    
                // Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
                for attribute in attributes {
                    attribute.frame.origin.x = leftInset
                    leftInset = attribute.frame.maxX + minimumInteritemSpacing
                }
            }
    
            return layoutAttributes
        }
    
    }
    

    CollectionViewCell.swift

    import UIKit
    
    class CollectionViewCell: UICollectionViewCell {
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            contentView.backgroundColor = .cyan
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    }
    

    预期结果:


    #2。中心自动调整大小UICollectionViewCells

    下面的实现展示了如何使用UICollectionViewLayoutlayoutAttributesForElements(in:)UICollectionViewFlowLayoutestimatedItemSizeUILabelpreferredMaxLayoutWidth来使UICollectionView的单元居中:

    CollectionViewController.swift

    import UIKit
    
    class CollectionViewController: UICollectionViewController {
    
        let array = ["1", "1 2", "1 2 3 4 5 6 7 8", "1 2 3 4 5 6 7 8 9 10 11", "1 2 3", "1 2 3 4", "1 2 3 4 5 6", "1 2 3 4 5 6 7 8 9 10", "1 2 3 4", "1 2 3 4 5 6 7", "1 2 3 4 5 6 7 8 9", "1", "1 2 3 4 5", "1", "1 2 3 4 5 6"]
    
        let columnLayout = FlowLayout(
            minimumInteritemSpacing: 10,
            minimumLineSpacing: 10,
            sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
        )
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            title = "Center cells"
    
            collectionView?.collectionViewLayout = columnLayout
            collectionView?.contentInsetAdjustmentBehavior = .always
            collectionView?.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
        }
    
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return array.count
        }
    
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
            cell.label.text = array[indexPath.row]
            return cell
        }
    
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            collectionView?.collectionViewLayout.invalidateLayout()
            super.viewWillTransition(to: size, with: coordinator)
        }
    
    }
    

    FlowLayout.swift

    import UIKit
    
    class FlowLayout: UICollectionViewFlowLayout {
    
        required init(minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
            super.init()
    
            estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
            self.minimumInteritemSpacing = minimumInteritemSpacing
            self.minimumLineSpacing = minimumLineSpacing
            self.sectionInset = sectionInset
            sectionInsetReference = .fromSafeArea
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
            guard scrollDirection == .vertical else { return layoutAttributes }
    
            // Filter attributes to compute only cell attributes
            let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })
    
            // Group cell attributes by row (cells with same vertical center) and loop on those groups
            for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
                // Get the total width of the cells on the same row
                let cellsTotalWidth = attributes.reduce(CGFloat(0)) { (partialWidth, attribute) -> CGFloat in
                    partialWidth + attribute.size.width
                }
    
                // Calculate the initial left inset
                let totalInset = collectionView!.safeAreaLayoutGuide.layoutFrame.width - cellsTotalWidth - sectionInset.left - sectionInset.right - minimumInteritemSpacing * CGFloat(attributes.count - 1)
                var leftInset = (totalInset / 2 * 10).rounded(.down) / 10 + sectionInset.left
    
                // Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
                for attribute in attributes {
                    attribute.frame.origin.x = leftInset
                    leftInset = attribute.frame.maxX + minimumInteritemSpacing
                }
            }
    
            return layoutAttributes
        }
    
    }
    

    CollectionViewCell.swift

    import UIKit
    
    class CollectionViewCell: UICollectionViewCell {
    
        let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            contentView.backgroundColor = .orange
            label.preferredMaxLayoutWidth = 120
            label.numberOfLines = 0
    
            contentView.addSubview(label)
            label.translatesAutoresizingMaskIntoConstraints = false
            contentView.layoutMarginsGuide.topAnchor.constraint(equalTo: label.topAnchor).isActive = true
            contentView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
            contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true
            contentView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    }
    

    预期结果:


    【讨论】:

    • 这是这里最全面的答案,应该点赞。
    【解决方案4】:

    如果有人有一个包含 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;
    }
    

    【讨论】:

      【解决方案5】:

      这可以通过从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
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        我已将 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;
        }
        

        希望它可以帮助某人:)

        【讨论】:

          【解决方案7】:

          我制作了 Swift 4 版本的 Kyle Truscott 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]
              }
          }
          

          【讨论】:

            【解决方案8】:

            只需链接此流程布局。也可以居中、左、右对齐。

             //
                //  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;
                }
            
            
            }
            

            【讨论】:

              【解决方案9】:

              Swift 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
                      }
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-07-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多