删除每个 UICollectionViewCell 之间的间距并将其移动到 UICollectionView 的外部单元格
Remove spacing between each UICollectionViewCell and shift it to the outer cells of the UICollectionView
抱歉,如果我的问题标题不清楚,但本质上我想制作一个像下面的 Medium 应用程序一样的 UICollectionView:
我制作了一个 UICollectionView,到目前为止它看起来是这样的:
我想减小每个单元格(红线)之间的间距,使它们靠得更近,并且在 collectionview 的两侧和边框上的单元格之间有 space。我用过 minimumInteritemSpacing
和 minimumLineSpacing
但它们对红色 space 完全没有影响。
这是我的代码:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let data = ["Autos", "Cleaning", "Technology", "Business", "Sports", "Childcare", "Airsoft", "Cycling", "Fitness", "Baseball", "Basketball", "Bird Watching", "Bodybuilding", "Camping", "Dowsing", "Driving", "Fishing", "Flying", "Flying Disc", "Foraging", "Freestyle Football", "Gardeing", "Geocaching", "Ghost hunting", "Grafitti", "Handball", "High-power rocketry", "Hooping", "Horseback riding", "Hunting"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let layout = UICollectionViewFlowLayout.init()
layout.scrollDirection = .vertical
layout.minimumInteritemSpacing = 5.0
layout.minimumLineSpacing = 20.0
let collectionView = UICollectionView.init(frame: self.view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(collectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionView.backgroundColor = .white
self.view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 18.0).isActive = true
collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -18.0).isActive = true
collectionView.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor).isActive = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! collectionViewCell
cell.textLabel.text = data[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let messageText = data[indexPath.row]
let size = CGSize.init(width: collectionView.frame.size.width, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimatedFrame = NSString.init(string: messageText).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 15.0, weight: .regular)], context: nil)
return CGSize.init(width: estimatedFrame.width + 20.0, height: estimatedFrame.height + 20.0)
}
}
class collectionViewCell: UICollectionViewCell {
var textLabel: UILabel = {
let label = UILabel.init()
label.font = UIFont.systemFont(ofSize: 15.0, weight: .regular)
label.textColor = UIColor.black
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(textLabel)
textLabel.translatesAutoresizingMaskIntoConstraints = false
textLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
textLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
setupShadow()
}
func setupShadow() {
self.contentView.backgroundColor = .white
self.contentView.layer.cornerRadius = 2.0
self.contentView.clipsToBounds = true
let shadowSize : CGFloat = 1.0
let shadowPath = UIBezierPath(rect: CGRect(x: -shadowSize / 2,
y: -shadowSize / 2,
width: self.contentView.frame.size.width + shadowSize,
height: self.contentView.frame.size.height + shadowSize))
self.contentView.layer.masksToBounds = false
self.contentView.layer.shadowColor = UIColor.black.cgColor
self.contentView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
self.contentView.layer.shadowOpacity = 0.5
self.contentView.layer.shadowPath = shadowPath.cgPath
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这可以通过 UICollectionViewFlowLayout
的子类自定义布局来实现。这是布局和演示 CollectionView/Cell 实现。
已更新ViewController.swift
class ViewController: UIViewController {
private var collectionView: UICollectionView!
private let demoLabel = UILabel()
private let minCellSpacing: CGFloat = 16.0
private var maxCellWidth: CGFloat!
var data: [String] = ["Tech", "Design", "Humor", "Travel", "Music", "Writing", "Social Media", "Life", "Education", "Edtech", "Education Reform", "Photography", "Startup", "Poetry", "Women In Tech", "Female Founders", "Business", "Fiction", "Love", "Food", "Sports", "Autos", "Cleaning", "Technology", "Business", "Sports", "Childcare", "Airsoft", "Cycling", "Fitness", "Baseball", "Basketball", "Bird Watching", "Bodybuilding", "Camping", "Dowsing", "Driving", "Fishing", "Flying", "Flying Disc", "Foraging", "Freestyle Football", "Gardeing", "Geocaching", "Ghost hunting", "Grafitti", "Handball", "High-power rocketry", "Hooping", "Horseback riding", "Hunting"]
override func viewDidLoad() {
super.viewDidLoad()
self.maxCellWidth = UIScreen.main.bounds.width - (minCellSpacing * 2)
self.view.backgroundColor = .white
self.demoLabel.font = CollectionViewCell().label.font
let layout = FlowLayout()
layout.sectionInset = UIEdgeInsets(top: self.minCellSpacing, left: 2.0, bottom: self.minCellSpacing, right: 2.0)
layout.minimumInteritemSpacing = self.minCellSpacing
layout.minimumLineSpacing = 16.0
collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
collectionView.backgroundColor = .clear
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
collectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor).isActive = true
// Leading/Trailing gutter CellSpacing+ShadowWidth
collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: minCellSpacing + layout.sectionInset.left).isActive = true
collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -(minCellSpacing + layout.sectionInset.right)).isActive = true
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CollectionViewCell
cell.label.text = self.data[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
self.demoLabel.text = self.data[indexPath.item]
self.demoLabel.sizeToFit()
return CGSize(width: min(self.demoLabel.frame.width + 16, self.maxCellWidth), height: 36.0)
}
}
FlowLayout.swift
class FlowLayout: UICollectionViewFlowLayout {
private var attribs = [IndexPath: UICollectionViewLayoutAttributes]()
override func prepare() {
self.attribs.removeAll()
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var updatedAttributes = [UICollectionViewLayoutAttributes]()
let sections = self.collectionView?.numberOfSections ?? 0
var indexPath = IndexPath(item: 0, section: 0)
while (indexPath.section < sections) {
guard let items = self.collectionView?.numberOfItems(inSection: indexPath.section) else { continue }
while (indexPath.item < items) {
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)
}
indexPath.item += 1
}
indexPath = IndexPath(item: 0, section: indexPath.section + 1)
}
return updatedAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
if let attributes = attribs[indexPath] {
return attributes
}
var rowCells = [UICollectionViewLayoutAttributes]()
var collectionViewWidth: CGFloat = 0
if let collectionView = collectionView {
collectionViewWidth = collectionView.bounds.width - collectionView.contentInset.left
- collectionView.contentInset.right
}
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 startIndex = indexPath.row
while true {
let lastIndex = startIndex - 1
if lastIndex < 0 {
break
}
let prevPath = IndexPath(row: lastIndex, 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) {
startIndex = lastIndex
} 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 rowCells
var cellIndex = startIndex
while cellIndex < totalRows {
let cellPath = IndexPath(row: cellIndex, section: indexPath.section)
if let cellAttributes = super.layoutAttributesForItem(at: cellPath),
cellAttributes.frame.intersects(rowTestFrame),
let cellAttributesCopy = cellAttributes.copy() as? UICollectionViewLayoutAttributes {
rowCells.append(cellAttributesCopy)
cellIndex += 1
} else {
break
}
}
let flowDelegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout
let selector = #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:))
let delegateSupportsInteritemSpacing = flowDelegate?.responds(to: selector) ?? false
var interitemSpacing = minimumInteritemSpacing
// Check for minimumInteritemSpacingForSectionAtIndex support
if let collectionView = collectionView, delegateSupportsInteritemSpacing && rowCells.count > 0 {
interitemSpacing = flowDelegate?.collectionView?(collectionView,
layout: self,
minimumInteritemSpacingForSectionAt: indexPath.section) ?? 0
}
let aggregateInteritemSpacing = interitemSpacing * CGFloat(rowCells.count - 1)
var aggregateItemWidths: CGFloat = 0
for itemAttributes in rowCells {
aggregateItemWidths += itemAttributes.frame.width
}
let alignmentWidth = aggregateItemWidths + aggregateInteritemSpacing
let alignmentXOffset: CGFloat = (collectionViewWidth - alignmentWidth) / 2
var previousFrame: CGRect = .zero
for itemAttributes in rowCells {
var itemFrame = itemAttributes.frame
if previousFrame.equalTo(.zero) {
itemFrame.origin.x = alignmentXOffset
} else {
itemFrame.origin.x = previousFrame.maxX + interitemSpacing
}
itemAttributes.frame = itemFrame
previousFrame = itemFrame
attribs[itemAttributes.indexPath] = itemAttributes
}
return attribs[indexPath]
}
}
CollectionViewCell.swift
class CollectionViewCell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.label.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(label)
self.contentView.addConstraints([
NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: contentView,
attribute: .leading, multiplier: 1.0, constant: 8.0),
NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: contentView,
attribute: .top, multiplier: 1.0, constant: 8.0),
NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .equal, toItem: label,
attribute: .trailing, multiplier: 1.0, constant: 8.0),
NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: label,
attribute: .bottom, multiplier: 1.0, constant: 8.0)])
self.backgroundColor = .white
self.label.textColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)
self.layer.cornerRadius = 3.0
self.layer.shadowColor = UIColor.darkGray.cgColor
self.layer.shadowOffset = CGSize(width: 0.1, height: 0.2)
self.layer.shadowOpacity = 0.28
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这是最后的UI
抱歉,如果我的问题标题不清楚,但本质上我想制作一个像下面的 Medium 应用程序一样的 UICollectionView:
我制作了一个 UICollectionView,到目前为止它看起来是这样的:
我想减小每个单元格(红线)之间的间距,使它们靠得更近,并且在 collectionview 的两侧和边框上的单元格之间有 space。我用过 minimumInteritemSpacing
和 minimumLineSpacing
但它们对红色 space 完全没有影响。
这是我的代码:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let data = ["Autos", "Cleaning", "Technology", "Business", "Sports", "Childcare", "Airsoft", "Cycling", "Fitness", "Baseball", "Basketball", "Bird Watching", "Bodybuilding", "Camping", "Dowsing", "Driving", "Fishing", "Flying", "Flying Disc", "Foraging", "Freestyle Football", "Gardeing", "Geocaching", "Ghost hunting", "Grafitti", "Handball", "High-power rocketry", "Hooping", "Horseback riding", "Hunting"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let layout = UICollectionViewFlowLayout.init()
layout.scrollDirection = .vertical
layout.minimumInteritemSpacing = 5.0
layout.minimumLineSpacing = 20.0
let collectionView = UICollectionView.init(frame: self.view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(collectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionView.backgroundColor = .white
self.view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 18.0).isActive = true
collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -18.0).isActive = true
collectionView.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor).isActive = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! collectionViewCell
cell.textLabel.text = data[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let messageText = data[indexPath.row]
let size = CGSize.init(width: collectionView.frame.size.width, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimatedFrame = NSString.init(string: messageText).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 15.0, weight: .regular)], context: nil)
return CGSize.init(width: estimatedFrame.width + 20.0, height: estimatedFrame.height + 20.0)
}
}
class collectionViewCell: UICollectionViewCell {
var textLabel: UILabel = {
let label = UILabel.init()
label.font = UIFont.systemFont(ofSize: 15.0, weight: .regular)
label.textColor = UIColor.black
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(textLabel)
textLabel.translatesAutoresizingMaskIntoConstraints = false
textLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
textLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
setupShadow()
}
func setupShadow() {
self.contentView.backgroundColor = .white
self.contentView.layer.cornerRadius = 2.0
self.contentView.clipsToBounds = true
let shadowSize : CGFloat = 1.0
let shadowPath = UIBezierPath(rect: CGRect(x: -shadowSize / 2,
y: -shadowSize / 2,
width: self.contentView.frame.size.width + shadowSize,
height: self.contentView.frame.size.height + shadowSize))
self.contentView.layer.masksToBounds = false
self.contentView.layer.shadowColor = UIColor.black.cgColor
self.contentView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
self.contentView.layer.shadowOpacity = 0.5
self.contentView.layer.shadowPath = shadowPath.cgPath
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这可以通过 UICollectionViewFlowLayout
的子类自定义布局来实现。这是布局和演示 CollectionView/Cell 实现。
已更新ViewController.swift
class ViewController: UIViewController {
private var collectionView: UICollectionView!
private let demoLabel = UILabel()
private let minCellSpacing: CGFloat = 16.0
private var maxCellWidth: CGFloat!
var data: [String] = ["Tech", "Design", "Humor", "Travel", "Music", "Writing", "Social Media", "Life", "Education", "Edtech", "Education Reform", "Photography", "Startup", "Poetry", "Women In Tech", "Female Founders", "Business", "Fiction", "Love", "Food", "Sports", "Autos", "Cleaning", "Technology", "Business", "Sports", "Childcare", "Airsoft", "Cycling", "Fitness", "Baseball", "Basketball", "Bird Watching", "Bodybuilding", "Camping", "Dowsing", "Driving", "Fishing", "Flying", "Flying Disc", "Foraging", "Freestyle Football", "Gardeing", "Geocaching", "Ghost hunting", "Grafitti", "Handball", "High-power rocketry", "Hooping", "Horseback riding", "Hunting"]
override func viewDidLoad() {
super.viewDidLoad()
self.maxCellWidth = UIScreen.main.bounds.width - (minCellSpacing * 2)
self.view.backgroundColor = .white
self.demoLabel.font = CollectionViewCell().label.font
let layout = FlowLayout()
layout.sectionInset = UIEdgeInsets(top: self.minCellSpacing, left: 2.0, bottom: self.minCellSpacing, right: 2.0)
layout.minimumInteritemSpacing = self.minCellSpacing
layout.minimumLineSpacing = 16.0
collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
collectionView.backgroundColor = .clear
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
collectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor).isActive = true
// Leading/Trailing gutter CellSpacing+ShadowWidth
collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: minCellSpacing + layout.sectionInset.left).isActive = true
collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -(minCellSpacing + layout.sectionInset.right)).isActive = true
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CollectionViewCell
cell.label.text = self.data[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
self.demoLabel.text = self.data[indexPath.item]
self.demoLabel.sizeToFit()
return CGSize(width: min(self.demoLabel.frame.width + 16, self.maxCellWidth), height: 36.0)
}
}
FlowLayout.swift
class FlowLayout: UICollectionViewFlowLayout {
private var attribs = [IndexPath: UICollectionViewLayoutAttributes]()
override func prepare() {
self.attribs.removeAll()
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var updatedAttributes = [UICollectionViewLayoutAttributes]()
let sections = self.collectionView?.numberOfSections ?? 0
var indexPath = IndexPath(item: 0, section: 0)
while (indexPath.section < sections) {
guard let items = self.collectionView?.numberOfItems(inSection: indexPath.section) else { continue }
while (indexPath.item < items) {
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)
}
indexPath.item += 1
}
indexPath = IndexPath(item: 0, section: indexPath.section + 1)
}
return updatedAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
if let attributes = attribs[indexPath] {
return attributes
}
var rowCells = [UICollectionViewLayoutAttributes]()
var collectionViewWidth: CGFloat = 0
if let collectionView = collectionView {
collectionViewWidth = collectionView.bounds.width - collectionView.contentInset.left
- collectionView.contentInset.right
}
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 startIndex = indexPath.row
while true {
let lastIndex = startIndex - 1
if lastIndex < 0 {
break
}
let prevPath = IndexPath(row: lastIndex, 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) {
startIndex = lastIndex
} 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 rowCells
var cellIndex = startIndex
while cellIndex < totalRows {
let cellPath = IndexPath(row: cellIndex, section: indexPath.section)
if let cellAttributes = super.layoutAttributesForItem(at: cellPath),
cellAttributes.frame.intersects(rowTestFrame),
let cellAttributesCopy = cellAttributes.copy() as? UICollectionViewLayoutAttributes {
rowCells.append(cellAttributesCopy)
cellIndex += 1
} else {
break
}
}
let flowDelegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout
let selector = #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:))
let delegateSupportsInteritemSpacing = flowDelegate?.responds(to: selector) ?? false
var interitemSpacing = minimumInteritemSpacing
// Check for minimumInteritemSpacingForSectionAtIndex support
if let collectionView = collectionView, delegateSupportsInteritemSpacing && rowCells.count > 0 {
interitemSpacing = flowDelegate?.collectionView?(collectionView,
layout: self,
minimumInteritemSpacingForSectionAt: indexPath.section) ?? 0
}
let aggregateInteritemSpacing = interitemSpacing * CGFloat(rowCells.count - 1)
var aggregateItemWidths: CGFloat = 0
for itemAttributes in rowCells {
aggregateItemWidths += itemAttributes.frame.width
}
let alignmentWidth = aggregateItemWidths + aggregateInteritemSpacing
let alignmentXOffset: CGFloat = (collectionViewWidth - alignmentWidth) / 2
var previousFrame: CGRect = .zero
for itemAttributes in rowCells {
var itemFrame = itemAttributes.frame
if previousFrame.equalTo(.zero) {
itemFrame.origin.x = alignmentXOffset
} else {
itemFrame.origin.x = previousFrame.maxX + interitemSpacing
}
itemAttributes.frame = itemFrame
previousFrame = itemFrame
attribs[itemAttributes.indexPath] = itemAttributes
}
return attribs[indexPath]
}
}
CollectionViewCell.swift
class CollectionViewCell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.label.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(label)
self.contentView.addConstraints([
NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: contentView,
attribute: .leading, multiplier: 1.0, constant: 8.0),
NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: contentView,
attribute: .top, multiplier: 1.0, constant: 8.0),
NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .equal, toItem: label,
attribute: .trailing, multiplier: 1.0, constant: 8.0),
NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: label,
attribute: .bottom, multiplier: 1.0, constant: 8.0)])
self.backgroundColor = .white
self.label.textColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)
self.layer.cornerRadius = 3.0
self.layer.shadowColor = UIColor.darkGray.cgColor
self.layer.shadowOffset = CGSize(width: 0.1, height: 0.2)
self.layer.shadowOpacity = 0.28
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这是最后的UI