Improper width of cell in collectionview - swift

[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

Related

UICollectionView horizontal scrolling ui bug

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

UICollectionView in UIControl - delegate & datasource

I've added a UICollectionView to a UIControl (which is being called in a UITableViewController section header) and the UICollectionView is appearing (background color shows), but the datasource and delegate seem to be ignored - no cells are showing and a print statement in my dequeueReusableCell function isn't being called (but one in the UIControl init method is. Can you tell me what I'm missing here?
My UIControl:
// Stripped down code
final class My_Control: UIControl {
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(myCV)
}
private let myCV: Custom_CV = {
let cv: Custom_CV = Custom_CV()
return cv
}()
}
My Collection View:
final class Custom_CV: UIControl, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellIdentifier)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
cell.backgroundColor = .red
print("Building cell") // Not being called
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 72.0, height: 72.0)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//
}
private let cellIdentifier: String = "cellIdentifier"
private let collectionView: UICollectionView = {
let layout: UICollectionViewLayout = UICollectionViewLayout()
let collectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = .white
return collectionView
}()
}
I think I'm missing something obvious, but I don't see it. I'm expecting to see 10 red squares and 10 print statements, but I get nothing there. But as I said - it's definitely being called because the a print statement in My_CV's init was being called, and I can see the .white background for the collectionview in My_Control.

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

Collectionview in collectionview cell fitting to content

I have a collectionview with two sections, each sections have one item. The cell in section contains collectionview and I need to fit height cell to content collectionview.
My first section has a fixed height to 120 because it's horizontal collectionview and height not changed. But for second section number of elements can be changed and I need to display all items (collectionview have scrollEnabled to false and scrolldirecton to vertical).
My problem is I need to display a collectionview with two scrolldirection depends sections and display all items for vertical collectionview.
When I do collectionview.collectionViewLayout.collectionViewContentSize.height I have the good height but I don't know how passed this height to parent viewcontroller. I search a dynamic solution to fixed this.
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// MARK: - UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return presenter.numberOfSections // == 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return presenter.numberOfItemsInSection // == 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueCell(InlineDisplayCollectionViewCell.self, for: indexPath)
configureCell(cell, at: indexPath)
return cell
}
// MARK: - UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let type = presenter.typeAtIndexPath(indexPath)
switch type {
case .city:
return CGSize(width: collectionView.frame.width, height: 120)
case .suggestion:
return CGSize(width: collectionView.frame.width, height: 820)
default:
return CGSize.zero
}
}
}
class InlineDisplayCollectionViewCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
var itemType: ItemModelCollectionType! = .city {
didSet {
updateCell()
}
}
var cities: [City]? {
didSet {
collectionView.reloadData()
}
}
var suggestions: [Suggestion]? {
didSet {
collectionView.reloadData()
}
}
override func awakeFromNib() {
super.awakeFromNib()
configureView()
}
// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemType == .city ? cities?.count ?? 0 : suggestions?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: UICollectionViewCell
if itemType == .city {
let cityCell = collectionView.dequeueCell(CityCollectionViewCell.self, for: indexPath)
configure(cell: cityCell, at: indexPath)
cell = cityCell
} else {
let suggestionCell = collectionView.dequeueCell(SquareCollectionViewCell.self, for: indexPath)
configure(cell: suggestionCell, at: indexPath)
cell = suggestionCell
}
return cell
}
// MARK: - UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return itemType == .city ? 10 : 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return itemType == .city ? 0 : 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size: CGSize
if itemType == .city {
size = CGSize(width: 100, height: 120)
} else {
let width = (collectionView.frame.width - (collectionView.contentInset.left + collectionView.contentInset.right) - 10) * 0.5
let height = width
size = CGSize(width: width, height: height)
}
return size
}
// MARK: - Private functions
private func configureView() {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
collectionView.registerNib(CityCollectionViewCell.self, bundle: nil)
collectionView.registerNib(SquareCollectionViewCell.self, bundle: nil)
}
private func updateCell() {
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = itemType == .city ? .horizontal : .vertical
}
collectionView.isScrollEnabled = itemType == .city
}
private func configure(cell: CityCollectionViewCell, at indexPath: IndexPath) {
guard let city = cities?[indexPath.row] else { return }
cell.configure(with: city)
}
private func configure(cell: SquareCollectionViewCell, at indexPath: IndexPath) {
guard let suggestion = suggestions?[indexPath.row] else { return }
cell.configure(with: suggestion)
}
}
You can't do that. CollectionView cells are dequeued and reused dynamically. The best way to go about it is to use different types of cells within the same CollectionView.
Please check the questions I have posted on this topic: Get the sum of all collectionViewCells' heights inside a CollectionView and Multiple Cells for Comments and Reply in a CollectionView
Unfortunately you can't do something like that. But you can have different types of cells inside your CV. Also you can modify that in the function called "cellForItemAt".

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