Each cell has a title with a label and it's supposed to be centered in the cell. When the view controller first appears each label is showed correctly, however when I scroll down the first cells move the label to the upper left and when I scroll up the last cells move the labels as well. How can I set each label to stay fixed in its initial position?
class TopicsVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UISearchBarDelegate {
override func viewDidLoad() {
self.filteredNames = self.names
self.collectionView.register(SearchCollectionViewCell.self, forCellWithReuseIdentifier: "SearchCollectionViewCell.identifier")
self.collectionView.register(TextCollectionViewCell.self, forCellWithReuseIdentifier: "TextCollectionViewCell.identifier")
if let collectionViewLayout = self.collectionView.collectionViewLayout as? RSKCollectionViewRetractableFirstItemLayout {
collectionViewLayout.firstItemRetractableAreaInset = UIEdgeInsets(top: 8.0, left: 0.0, bottom: 8.0, right: 0.0)
}
}
fileprivate let names = ["F", "N", "S", "M", "R", "T", "A", "M", "T", "F"]
fileprivate let images = ["f.png", "n.png", "s.png", "m.png", "r.png", "t.png", "a.png","m.png","t.png","f.png"]
// MARK: - Layout
internal override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard self.readyForPresentation == false else {
return
}
self.readyForPresentation = true
let searchItemIndexPath = IndexPath(item: 0, section: 0)
self.collectionView.contentOffset = CGPoint(x: 0.0, y: self.collectionView(self.collectionView, layout: self.collectionView.collectionViewLayout, sizeForItemAt: searchItemIndexPath).height)
}
// MARK: - UICollectionViewDataSource
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.section {
case 0:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchCollectionViewCell.identifier", for: indexPath) as! SearchCollectionViewCell
cell.searchBar.delegate = self
cell.searchBar.searchBarStyle = .minimal
cell.searchBar.placeholder = "Search a topic"
return cell
case 1:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TextCollectionViewCell.identifier", for: indexPath) as! TextCollectionViewCell
let name = self.filteredNames[indexPath.item]
cell.colorView.layer.cornerRadius = 10.0
cell.colorView.layer.masksToBounds = true
//cell.colorView.backgroundColor = self.colors[name]
cell.colorView.backgroundColor = UIColor(patternImage: UIImage(named: images[indexPath.row])!)
cell.colorView.layer.contents = UIImage.init(named:images[indexPath.row])!.cgImage
cell.label.textColor = UIColor.white
cell.label.font = UIFont(name:"HelveticaNeue-Bold", size: 24.0)
cell.label.sizeToFit()
cell.label.numberOfLines = 2
cell.label.textAlignment = .center
cell.label.text = name
return cell
default:
assert(false)
}
}
internal func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0:
return 1
case 1:
return self.filteredNames.count
default:
assert(false)
}
}
internal func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
// MARK: - UICollectionViewDelegateFlowLayout
internal func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
switch section {
case 0:
return UIEdgeInsets.zero
case 1:
return UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
default:
assert(false)
}
}
internal func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
internal func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
internal func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.section {
case 0:
let itemWidth = collectionView.frame.width
let itemHeight: CGFloat = 150.0
return CGSize(width: itemWidth, height: itemHeight)
case 1:
let numberOfItemsInLine: CGFloat = 2
let inset = self.collectionView(collectionView, layout: collectionViewLayout, insetForSectionAt: indexPath.section)
let minimumInteritemSpacing = self.collectionView(collectionView, layout: collectionViewLayout, minimumInteritemSpacingForSectionAt: indexPath.section)
let itemWidth = (collectionView.frame.width - inset.left - inset.right - minimumInteritemSpacing * (numberOfItemsInLine - 1)) / numberOfItemsInLine
let itemHeight = itemWidth
return CGSize(width: itemWidth, height: itemHeight)
default:
assert(false)
}
}
// MARK: - UIScrollViewDelegate
internal func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
guard scrollView === self.collectionView else {
return
}
let indexPath = IndexPath(item: 0, section: 0)
guard let cell = self.collectionView.cellForItem(at: indexPath) as? SearchCollectionViewCell else {
return
}
guard cell.searchBar.isFirstResponder else {
return
}
cell.searchBar.resignFirstResponder()
}
// MARK: - UISearchBarDelegate
internal func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let oldFilteredNames = self.filteredNames!
if searchText.isEmpty {
self.filteredNames = self.names
}
else {
self.filteredNames = self.names.filter({ (name) -> Bool in
return name.hasPrefix(searchText)
})
}
self.collectionView.performBatchUpdates({
for (oldIndex, oldName) in oldFilteredNames.enumerated() {
if self.filteredNames.contains(oldName) == false {
let indexPath = IndexPath(item: oldIndex, section: 1)
self.collectionView.deleteItems(at: [indexPath])
}
}
for (index, name) in self.filteredNames.enumerated() {
if oldFilteredNames.contains(name) == false {
let indexPath = IndexPath(item: index, section: 1)
self.collectionView.insertItems(at: [indexPath])
}
}
}, completion: nil)
}
It's possible that the code: cell.label.sizeToFit()
inside your implementation of collectionView(_:, cellForItemAt:)
is causing the problem. I recommend deleting that line.
Reasoning: If you have already set up your label in Interface Builder to center the text in the cell, there's no need to update the label size here. Sizing to fit could cause the label to shrink, potentially resulting in the behavior you have described of seeming to move towards the top left.
(Even worse, the call to sizeToFit()
in your code above happens before you actually set the new text for the cell, so its results will be based on the text that was in the cell before it was reused.)