UICollectionView horizontal scrolling ui bug - swift

I have code like this for my collection view
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.isPagingEnabled = true
cv.showsHorizontalScrollIndicator = false
cv.backgroundColor = .white
return cv
}()
And I want to show 5 items in this collection view horizontally while paging having 4 items on a page. It works fine when I scroll to the next page which will contain my 2nd to 5th items but then comes the bug. If I just tap on the second page it will auto-scroll to my first page. This isn't an issue when I have a total of six items and the second-page shows 3rd to 6th items. It seems to occur when the second page has fewer items to display like in my first example.
Can anybody help me with this bug?
Here is an example
class VC: UIViewController {
// add collectionView to view in viewDidLoad
view.addSubView(collectionView)
}
extension VC: UICollectionViewDelegate { }
extension VC: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
return cell
}
}
extension VC: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
CGSize(width: collectionView.bounds.width / 3, height: collectionView.bounds.height)
}
}

Related

Improper width of cell in collectionview

[updated]
I have created collectionview containing cells with different captions and different widths. It works fine when I read collection on launch of application.
But when I add new cell during usage of the application it has standard, narrow, width.
When I again relaunch the application it will again have correct width.
After adding reloadData() it works fine for one cell. But when I have multiple cells they are drawn one on each other.
And here is the code:
override func viewDidLoad() {
super.viewDidLoad()
projectCollectionView.delegate = self
projectCollectionView.dataSource = self
projectCollectionView.register(UINib(nibName: "projectCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "projectCollectionViewCell")
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: projectCollectionViewCell.identifier, for: indexPath) as? projectCollectionViewCell
else {
return projectCollectionViewCell()
}
cell.projectButton.setTitle("a title", for: .normal)
projectCollection[indexPath.row].cellIndex = indexPath.row
cell.projectButton.sizeToFit()
cell.layer.bounds.size.width = cell.projectButton.layer.bounds.width
return cell
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
projectCollectionView.collectionViewLayout.invalidateLayout()
projectCollectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
var result: Int = 0
for i in 0...projects.count-1 {
if (projects[i].status>=2) {
result += 1
}
}
return result
}
When I remove the row: cell.projectButton.sizeToFit() it started to look like this:
try it :-
relod collectionview after adding element.
collectionview.reloaddata()
[edited]
add flowlayout to collectionview
add below code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let label = UILabel(frame: CGRect.zero)
label.text = textArray[indexPath.item]
label.sizeToFit()
return CGSize(width: label.frame.width, height: 32)
}
for more visit https://stackoverflow.com/a/53284536/12451658

How Can I align the colectionViewCells to the left?

I have a cUIolectionView that sometimes has only 1 item. If that is the case, the item is aligned in the middle, see picture:
But what I want, is that the item is aligned to the left.
I have found this answer on StackOverflow, leftAlign cells in UIColectioniew - StackOverFlow, but when I added the class given in the accepted answer to my codeBase, and added this to the viewControler:
let leftAlignLayout = AlignedCollectionViewFlowLayout(horizontalAlignment: .left, verticalAlignment: .top)
gamesColectionView.collectionViewLayout = leftAlignLayout
it did align the way I wanted, but it made the cells very small(see picture)
I also tried to add this from Github: AlignedCollectionViewFlowLayout - GitHub but that had the same result.
I tried fixing that bug by adding this to my viewController File:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 130, height: 130)
}
but that didn't work.
does anybody have a suggestion on how I can fix this? I don't know what I did wrong or what I forgot to do.
thanks a lot!
BSM
You can manage your cell with below code. Do not forget to add UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
// your code here
}
Try this:
var layout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 8.0
layout.minimumInteritemSpacing = 8.0
let width = (UIScreen.main.bounds.size.width - 40.0) / 4
layout.estimatedItemSize = CGSize(width: width, height: 98.0)
return layout
}()
And:
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView?.collectionViewLayout = layout
self.collectionView!.contentInset = UIEdgeInsets(top: 0, left: 0, bottom:0, right: 0)
}
You don't need to use sizeForItemAt. If you want more you can look my project. Check constraints of your imageView with it's superview
You don't need to do anything special to get the collectionViewCells left aligned.
If the constraints are proper, the cells will automatically start rendering from the left.
class VC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 130, height: 130)
}
}
Simply set the scrollDirection to vertical or horizontal as per your requirement within the storyboard itself.
There is no need to use any 3rd party library to get that working.

UICollectionView Location for Supplementary Header

I am looking to put a header on my collection view's sections. I have a collectionview embedded inside a tablebview. Using the section headers of the tableview puts the header too far from the content. Putting the section header as part of the collection view; however, is putting that header on top of the collection view cell itself. I am looking for some space between the section header and the section detail.
func setup() {
let layout = UICollectionViewFlowLayout()
layout.itemSize = THUMB_SIZE
layout.minimumLineSpacing = 25
layout.sectionInset.right = layout.itemSize.width / 2
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor(named: "collectionView")
collectionView.delegate = self
collectionView.dataSource = self
collectionView.remembersLastFocusedIndexPath = true
collectionView.register(MainCVCell.self, forCellWithReuseIdentifier: "cvCell")
self.addSubview(collectionView)
self.collectionView.register(HeaderCVCell.self, forCellWithReuseIdentifier: "headerCell")
}
// MARK: - Collectionview Delegate & Datasource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return topics.shows.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cvCell", for: indexPath) as! MainCVCell
cell.show = topics.shows[indexPath.row]
cell.selectedIndex = indexPath.row
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate.selectedMedia(showID: topics.shows[indexPath.row])
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let footerView = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath)
if kind == UICollectionView.elementKindSectionHeader {
let headerView = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath) as! HeaderCVCell
headerView.headerText = topics.title
return headerView
} else {
return footerView
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: 172, height: 60)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return .zero
}
Now for collection view header cell:
class HeaderCVCell: UICollectionViewCell {
var headerText:String! {
didSet {
setup()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func setup() {
let headerLabel = UILabel()
headerLabel.text = headerText
headerLabel.textColor = .white
headerLabel.sizeToFit()
self.subviews.forEach {$0.removeFromSuperview()}
self.addSubview(headerLabel)
}
}
One thing I noted by putting a background color on the header cell itself, is that the height of the header cell is equaling the height of the collection view. There does not though seem to be anyway that I can adjust the height of the header view cell independently of the collection view cells. If I change layout.itemSize it changes the size for both the header and the content. If I use layout.headerReferenceSize, it does not change anything. I am also using the delegate method as you can see from the code. Per the documentation it appears that it won't look at the height when in a horizontal scrolling situation such as this is. I have also tried to set the frame of the header cell directly but this is ignored.
Net, my section header is crashing into my regular content in the collection view. I suspect this might be because the height of the header is the height of the collection view and thus it is being positioned as high as it can go. I cannot change the height nor find any other way to separate the header from the content.
I have a similar set up and in my Header file, I have the size set with:
override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width / 2 + 60, height: frame.height)
}
And, if you want to create some spacing, you can use insets. Setting the top to a negative number should show some space after the header.
override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}

How can I programmatically insert UICollectionView into UITableViewCell ? [Swift]

Does anyone know how to integrate a collectionView into the top section of a tableView? I'm trying to get a top section/row that has horizontally scrolling images, and other section/rows below it to just be normal tableview rows. Like what you might see in an app like facebook messenger or tinder/bumble's matches/conversations page.
I understand how to produce UICollectionView and UITableView separately, but between the multiple functions/delegates/datasources, I can't figure out how to properly layer the code.
I am working programmatically rather than on storyboard. I know there are other posts on inserting UICollectionView in UITableViewCell, but they're either storyboard implementations, or on outdated versions of Swift - would be awesome if someone could do a run-through so that anyone looking in the future could also understand the logic even if some of the code becomes outdated.
declare collectionView in the TableViewCell swift file.
var viewPhotos: UICollectionView!
func reset(with cellData: CellData) {
self.data = cellData
viewPhotos.dataSource = self
viewPhotos.delegate = self
viewPhotos.reloadData()
}
in the init function or awakeFromNib check if photoCollectionView is initialized and add it to the cell.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
viewPhotos = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
viewPhotos.collectionViewLayout = layout
viewPhotos.showsHorizontalScrollIndicator = false
viewPhotos.isPagingEnabled = true
viewPhotos.register(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: PhotoCollectionViewCell.cellIdentifier)
Add UICollectionView Datasource and Delegate To the UITableViewCell
extension TableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection > section: Int) -> Int {
return photoUrls?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PhotoCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.cellIdentifier, for: indexPath) as! PhotoCollectionViewCell
if let value = photoUrls {
cell.reset(with url: value[indexPath.row]);
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: kPhotoWidth, height: kPhotoHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets.init(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout > collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
}
PhotoCollectionViewCell
create a PhotoCollectionViewCell derived from UICollectionViewCell
and add a UIImageView, and make all edge constraints to 0 to the
superview. load UIImageView based on photoUrl.

CollectionView Cell clicked expand new view

I'm trying to create a demo app that has a TabBarViewController as well as 4 tabs ( 4 Different ViewControllers on the storyboard)
On the first Tab which is the first ViewController, I have a collectionView
with Cells that have ImageView and few Labels.
What I'm trying to do is, when you click on a Cell, It expands to the whole screen, and the view still stays in the first Tab from the Tab Bar,
just now that the view inside the ViewController has changed to a new view that is an expanded view of the Cell that was selected, and when you scroll down the collection view continues, and you can click another cell and it expands and so on...while you can at any time press back and it shows the last cell that was expanded in the view
I hope I was clear enough about what I'm trying to do. now ill add the code of what I've got already
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var imagesArray = []
var imageSizes = [CGSize]()
var labelsArray = [""]
var descArray = [""]
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.delegate = self
self.collectionView.dataSource = self
for i in 0..<imagesArray.count {
let currImg = imagesArray[i]
let currHeight = currImg.size.height
let currSize = CGSize(width: 150, height: currHeight + 125)
imageSizes.append(currSize)
}
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagesArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionVCe
cell.cellImg.image = imagesArray[indexPath.row]
cell.descLabel.text = descArray[1]
cell.itemLabel.text = labelsArray[0]
cell.cellImg.contentMode = .scaleAspectFill
cell.layer.cornerRadius = 9
cell.backgroundColor = UIColor.red
cell.cellImg.backgroundColor = UIColor.blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return imageSizes[indexPath.row]
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 25, 0, 25)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.frame = collectionView.frame
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You have reload the collection view and also call size delegate of collection view