如何删除 UICollectionView Flow 布局大小中的单元格空间?
Posted
技术标签:
【中文标题】如何删除 UICollectionView Flow 布局大小中的单元格空间?【英文标题】:How to remove cell space in UICollectionView Flow layout size? 【发布时间】:2020-05-05 04:43:07 【问题描述】:我正在将集合视图布局自定义为 3 列 [左侧 2 个单元格和右侧 1 个纵向单元格]。我无法移除 let side 2 单元格的顶部空间。
代码:
import UIKit
extension UICollectionView
func getSize(noOfCellsInRow: Int, isPotrait: Bool = true)->CGSize
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let totalSpace = flowLayout.sectionInset.left
+ flowLayout.sectionInset.right
+ (flowLayout.minimumInteritemSpacing * CGFloat(noOfCellsInRow - 1))
let size = Int((self.bounds.width - totalSpace) / CGFloat(noOfCellsInRow))
return CGSize(width: size, height: isPotrait ? size+size : size)
class CollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 3
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.contentView.backgroundColor = .red
return cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
if indexPath.row == 1
return collectionView.getSize(noOfCellsInRow: 2,isPotrait: true)
else
return collectionView.getSize(noOfCellsInRow: 2,isPotrait: false)
对应代码的输出:
预期输出:
我怎样才能让它工作?任何想法都会对我有很大帮助。提前谢谢...
【问题讨论】:
【参考方案1】:我已经定制了 Apple 的 MosaicLayout 示例以适合您的预期输出。
这是自定义 UICollectionViewLayout,添加了 twoFiftyFifty
作为此段样式。
enum MosaicSegmentStyle
case twoFiftyFifty
case fullWidth
case fiftyFifty
case twoThirdsOneThird
case oneThirdTwoThirds
class MosaicLayout: UICollectionViewLayout
var contentBounds = CGRect.zero
var cachedAttributes = [UICollectionViewLayoutAttributes]()
/// - Tag: PrepareMosaicLayout
override func prepare()
super.prepare()
guard let collectionView = collectionView else return
// Reset cached information.
cachedAttributes.removeAll()
contentBounds = CGRect(origin: .zero, size: collectionView.bounds.size)
// For every item in the collection view:
// - Prepare the attributes.
// - Store attributes in the cachedAttributes array.
// - Combine contentBounds with attributes.frame.
let count = collectionView.numberOfItems(inSection: 0)
var currentIndex = 0
var segment: MosaicSegmentStyle = .fiftyFifty
var lastFrame: CGRect = .zero
let cvWidth = collectionView.bounds.size.width
while currentIndex < count
let segmentFrame = CGRect(x: 0, y: lastFrame.maxY + 1.0, width: cvWidth, height: (self.collectionView?.frame.width)!)
var segmentRects = [CGRect]()
switch segment
case .twoFiftyFifty:
let horizontalSlices = segmentFrame.dividedIntegral(fraction:0.5, from: .minXEdge)
let verticalSlices = horizontalSlices.first.dividedIntegral(fraction: 0.5, from: .minYEdge)
segmentRects = [verticalSlices.first, verticalSlices.second, horizontalSlices.second]
case .fullWidth:
segmentRects = [segmentFrame]
case .fiftyFifty:
let horizontalSlices = segmentFrame.dividedIntegral(fraction: 0.5, from: .minXEdge)
segmentRects = [horizontalSlices.first, horizontalSlices.second]
case .twoThirdsOneThird:
let horizontalSlices = segmentFrame.dividedIntegral(fraction: (2.0 / 3.0), from: .minXEdge)
let verticalSlices = horizontalSlices.second.dividedIntegral(fraction: 0.5, from: .minYEdge)
segmentRects = [horizontalSlices.first, verticalSlices.first, verticalSlices.second]
case .oneThirdTwoThirds:
let horizontalSlices = segmentFrame.dividedIntegral(fraction: (1.0 / 3.0), from: .minXEdge)
let verticalSlices = horizontalSlices.first.dividedIntegral(fraction: 0.5, from: .minYEdge)
segmentRects = [verticalSlices.first, verticalSlices.second, horizontalSlices.second]
// Create and cache layout attributes for calculated frames.
for rect in segmentRects
let attributes = UICollectionViewLayoutAttributes(forCellWith: IndexPath(item: currentIndex, section: 0))
attributes.frame = rect
cachedAttributes.append(attributes)
// contentBounds = contentBounds.union(lastFrame) contentBounds = contentBounds.union(rect)
currentIndex += 1
lastFrame = rect
// // Determine the next segment style.
// switch count - currentIndex
// case 1:
// segment = .fullWidth
// case 2:
// segment = .fiftyFifty
// default:
// switch segment
// case .fullWidth:
// segment = .fiftyFifty
// case .fiftyFifty:
// segment = .twoThirdsOneThird
// case .twoThirdsOneThird:
// segment = .oneThirdTwoThirds
// case .oneThirdTwoThirds:
// segment = .fiftyFifty
//
//
/// - Tag: CollectionViewContentSize
override var collectionViewContentSize: CGSize
return contentBounds.size
/// - Tag: ShouldInvalidateLayout
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
guard let collectionView = collectionView else return false
return !newBounds.size.equalTo(collectionView.bounds.size)
/// - Tag: LayoutAttributesForItem
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
return cachedAttributes[indexPath.item]
/// - Tag: LayoutAttributesForElements
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
var attributesArray = [UICollectionViewLayoutAttributes]()
// Find any cell that sits within the query rect.
guard let lastIndex = cachedAttributes.indices.last,
let firstMatchIndex = binSearch(rect, start: 0, end: lastIndex) else return attributesArray
// Starting from the match, loop up and down through the array until all the attributes
// have been added within the query rect.
for attributes in cachedAttributes[..<firstMatchIndex].reversed()
guard attributes.frame.maxY >= rect.minY else break
attributesArray.append(attributes)
for attributes in cachedAttributes[firstMatchIndex...]
guard attributes.frame.minY <= rect.maxY else break
attributesArray.append(attributes)
return attributesArray
// Perform a binary search on the cached attributes array.
func binSearch(_ rect: CGRect, start: Int, end: Int) -> Int?
if end < start return nil
let mid = (start + end) / 2
let attr = cachedAttributes[mid]
if attr.frame.intersects(rect)
return mid
else
if attr.frame.maxY < rect.minY
return binSearch(rect, start: (mid + 1), end: end)
else
return binSearch(rect, start: start, end: (mid - 1))
divideIntegral 扩展:
extension CGRect
func dividedIntegral(fraction: CGFloat, from fromEdge: CGRectEdge) -> (first: CGRect, second: CGRect)
let dimension: CGFloat
switch fromEdge
case .minXEdge, .maxXEdge:
dimension = self.size.width
case .minYEdge, .maxYEdge:
dimension = self.size.height
let distance = (dimension * fraction).rounded(.up)
var slices = self.divided(atDistance: distance, from: fromEdge)
switch fromEdge
case .minXEdge, .maxXEdge:
slices.remainder.origin.x += 1
slices.remainder.size.width -= 1
case .minYEdge, .maxYEdge:
slices.remainder.origin.y += 1
slices.remainder.size.height -= 1
return (first: slices.slice, second: slices.remainder)
MosaicCell 类:
class MosaicCell: UICollectionViewCell
static let identifer = "kMosaicCollectionViewCell"
var imageView = UIImageView()
var assetIdentifier: String?
override init(frame: CGRect)
super.init(frame: frame)
self.clipsToBounds = true
self.autoresizesSubviews = true
imageView.frame = self.bounds
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(imageView)
// Use a random background color.
let redColor = CGFloat(arc4random_uniform(255)) / 255.0
let greenColor = CGFloat(arc4random_uniform(255)) / 255.0
let blueColor = CGFloat(arc4random_uniform(255)) / 255.0
self.backgroundColor = UIColor(red: redColor, green: greenColor, blue: blueColor, alpha: 1.0)
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func prepareForReuse()
super.prepareForReuse()
imageView.image = nil
assetIdentifier = nil
你的 ViewController 类:
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout
override func viewDidLoad()
let mosaicLayout = MosaicLayout()
collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: mosaicLayout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.alwaysBounceVertical = true
collectionView.indicatorStyle = .white
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(MosaicCell.self, forCellWithReuseIdentifier: MosaicCell.identifer)
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 3
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MosaicCell.identifer, for: indexPath)
cell.contentView.backgroundColor = .red
return cell
这是 Apple 示例的链接:https://developer.apple.com/documentation/uikit/uicollectionview/customizing_collection_view_layouts
您需要根据需要配置以后的细分。
【讨论】:
很高兴为您提供帮助,@MountAin! 一个帮助,我不能做 3 行。我最多只能制作 2 行。我尝试了部分但不起作用。你能帮我制作 3 行 fullWidth 吗?这是视频streamable.com/b2yac5 这是我在代码segmentRects = [ CGRect(x: 0, y: lastFrame.maxY + 1.0, width: cvWidth, height: cvWidth/1.5)]
中修改的内容
第三行不在范围内
@MountAin,您在 Apple 示例代码中发现了一个错误。做得好!我已经更新了我的答案来解决这个问题。 contentBounds = contentBounds.union(lastFrame)
没有拾取最后一个单元格,所以我将其更改为 contentBounds = contentBounds.union(rect)
。我已经确认它对我有用。请让我知道它是否也为您解决了问题。【参考方案2】:
您需要自定义自己的自定义UICollectionViewLayout,如PinterestLayout,系统内置UICollectionViewFlowLayout
仅适用于网格布局。
【讨论】:
好的,谢谢。我会试试的以上是关于如何删除 UICollectionView Flow 布局大小中的单元格空间?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 UICollectionView Image 一张一张删除
UICollectionView如何删除单元格(相当于commitEditingStyle)?
如何从 UICollectionView 中正确删除单元格?
如何删除 UICollectionView 单元格中的单个对象?