UICollectionView don't show all content when scrolling horizontally - swift

In my case,I only have one Section,and the section has 11 items.The problem is I can not scroll it to the last items.I can only scroll a very small distance from left to the right direction and it give me a bounce back.
Here is my code:
class RecommendGameView: UIView {
//...
override func layoutSubviews() {
super.layoutSubviews()
let layout = gameCollectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: 80, height: 90)
gameCollectionView.alwaysBounceHorizontal = true
gameCollectionView.register(UINib(nibName: "CollectionGameCell", bundle: nil), forCellWithReuseIdentifier: kGameCellID)
gameCollectionView.contentInset = UIEdgeInsets(top: 0, left: kEdgeInsetMargin, bottom: 0, right: kEdgeInsetMargin)
}
}
extension RecommendGameView : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (groups?.count ?? 0)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = gameCollectionView.dequeueReusableCell(withReuseIdentifier: kGameCellID, for: indexPath) as! CollectionGameCell
cell.group = groups![indexPath.item]
return cell
}
}

Related

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".

UICollectionView not loading new cells when scrolling horizontally

I am placing a horizontal scroll UICollectionViewController inside of another UICollectionViewController cell. I am doing this because I want to create an image slider like effect.
Code for horizontal scroll UICollectionViewController:
class BusinessPhotosViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout{
var photos = [String]()
let cellId = "photos"
override func viewDidLoad() {
super.viewDidLoad()
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
}
collectionView?.backgroundColor = .red
collectionView?.showsVerticalScrollIndicator = false
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.alwaysBounceVertical = false
collectionView?.isPagingEnabled = true
collectionView?.isScrollEnabled = true
collectionView?.register(BusinessPhotoCells.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! BusinessPhotoCells
if photos.count != 0 {
let imageUrl = photos[indexPath.item]
let url = URL(string: imageUrl)
cell.logoImage.kf.setImage(with: url)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.height-10, height: view.frame.height-10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
}
In another class I add this horizontal scroll UICollectionViewController as such:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//ADDING SCROLL CELL HERE
if indexPath.section == 1 {
let scrollCell = collectionView.dequeueReusableCell(withReuseIdentifier: pictureCellId, for: indexPath)
scrollCell.backgroundColor = .blue
let bizVC = BusinessPhotosViewController(collectionViewLayout: UICollectionViewFlowLayout())
scrollCell.addSubview(bizVC.view)
bizVC.view.anchor(top: scrollCell.topAnchor, left: scrollCell.leftAnchor, bottom: scrollCell.bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: scrollCell.frame.width, height: scrollCell.frame.height)
return scrollCell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: listCellId, for: indexPath) as! CouponCell
return cell
}
Edit: Adding numberOfItemsInSection per comments
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 0
}
if section == 1 {
return 1
}
return coupons.count
}
I return 1 for this section because I only want 1 horizontal scroll cell...Below that horizontal scroll cell (in section 2) I then have a list of vertical scroll cells that work as expected.
This results in something like this where only 3 squares are loaded up not the 10 as expected:
EDIT: ADDING IMAGE OF WHAT MY VIEW LOOKS LIKE NOW

CollectionViewLayout Height

I want to custom a lot of layout size for each row of my collectionView by using UICollectionViewDelegateFlowLayout like below:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.frame.width
if indexPath.row == 0 {
return CGSize(width: width, height: 300)
}else if indexPath.row == 1 {
return dynamicSize // i want this size wrap its content
}
return CGSize(width: width, height:100)
}
I want dynamicSize that can wrap its content for some row.
Anyone know how to do this?
Here is image of cell that i want dynamic size to wrap it.
I suggest you to use tableview instead of collectionview. It is very easy for you.
And for cellForHeight returns UITableViewAutomaticDimension. It will calculate your cell height itself and adjust it accordingly.
Note: Make sure your constraints are proper.
Edit: By using collectionview with two columns.
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var text = "Do any additional setup after loading the view, typically from a nib."
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets{
return UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
let cellWidth = self.view.frame.size.width/2 - 10
let textHeight = text.height(withConstrainedWidth: cellWidth, font: UIFont.systemFont(ofSize: 16))
let constantHeight : CGFloat = 40 + 25 + 10 // top name view + bottom like button + margins height
let totHeight = constantHeight + textHeight
return CGSize(width: cellWidth, height: totHeight)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCollectionViewCell", for: indexPath) as! MyCollectionViewCell
cell.label.text = text
cell.layer.borderWidth = 1.0
cell.layer.borderColor = UIColor.black.cgColor
return cell
}
}
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)
return ceil(boundingBox.height)
}
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)
return ceil(boundingBox.width)
}
}
Output:
Use this delegate method. this will work for me.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: Your cellWidth, height: YourcellHeight);
}

Extra cell being added to last row in UICollection View

This is my UICollectionView code:
extension UserFeed: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
resizeCollectionView()
return feeds.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
userFeedDelegate?.didSelectFeed(feeds[indexPath.row])
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: UserFeedCollectionViewCell.kCellIdentifier, for: indexPath) as! UserFeedCollectionViewCell
print(indexPath.row)
cell.setup(feeds[indexPath.row])
return cell
}
}
An here is the setup for my collection view
feedLayout.sectionInset = UIEdgeInsets.zero
feedLayout.itemSize = CGSize(width: size, height: size)
feedLayout.scrollDirection = .vertical
feedLayout.minimumInteritemSpacing = 0
feedLayout.minimumLineSpacing = 0
In this instance the feeds.count is 2 and 2 cells being created by the amount of indexPath.row being printed: 0,1
However in the output 4 cells are being displayed with the last to be uninstantiated versions of the custom UICollectionView Cell type. If it helps I only saw this extra cells after adding scrolling to my view.
Thanks!
Edit:
My resize collection view function:
fileprivate func resizeCollectionView() {
let height:CGFloat = CGFloat(Int(ceil(Double(feeds.count)/2))) * size
frame = CGRect(x: 0, y: frame.origin.y, width: frame.width, height: height)
userFeedDelegate?.didChangedCollectionHeight()
}

Swift creating grid of many text fields?

Fairly new to swift and just getting into the design/app making aspect of it. So I want to make a 9X9 grid of text fields for a sudoku solving app, where each text field will take in a number and assign the value to a struct I have created. I am having a hard time finding the correct way to do this. I feel like I could just make 81 text boxes and assign each of them seperately, but I feel like as a programmer this is definitely the wrong way to go about this. Could anyone point me in the right direction?
Thanks!
UICollectionView is the way to go.
If you want a 9*9 grid with a header cell on top then something like this would work:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 82
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if indexPath.row == 0
{
return CGSize(width: screenWidth, height: screenWidth/3)
}
return CGSize(width: screenWidth/9, height: screenWidth/9);
}
That gives you:
Complete viewController with an embedded UICollectionView:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var collectionView : UICollectionView? // Optional
var screenSize : CGRect!
var screenWidth : CGFloat!
var screenHeight : CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
screenSize = self.view.frame
screenWidth = screenSize.width
screenHeight = screenSize.height
// Do any additional setup after loading the view, typically from a nib.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
layout.itemSize = CGSize(width: screenWidth/3, height: screenWidth/3)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
print(collectionView?.frame.width)
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
collectionView!.backgroundColor = UIColor.greenColor()
self.view.addSubview(collectionView!)
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 82
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if indexPath.row == 0
{
return CGSize(width: screenWidth, height: screenWidth/3)
}
return CGSize(width: screenWidth/9, height: screenWidth/9);
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! CollectionViewCell
if indexPath.row == 0
{
cell.backgroundColor = UIColor.lightGrayColor()
}else
{
cell.backgroundColor = UIColor.whiteColor()
}
cell.layer.borderColor = UIColor.blackColor().CGColor
cell.layer.borderWidth = 0.5
//cell.textLabel?.text = "\(indexPath.section):\(indexPath.row)"
return cell
}
}