Display UICollectionView with 2 columns of equal size square cells - ios

I'm trying to accomplish the following:
I've followed the answer in this question, but I get the image below:
This is the code for the collection view controller:
class MainMenuViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet weak var dateTimeLabel: MarqueeLabel!
#IBOutlet weak var iconButton: UIButton!
#IBOutlet var imageView: UIImageView!
var iconButtonTimer: Timer!
var upDateTimer: Timer!
var statusString: String!
var alertTitle: String!
var networkStatusString: String!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UINib.init(nibName: "MainMenuCell", bundle: nil), forCellWithReuseIdentifier: "MainMenuCell")
collectionView.collectionViewLayout = MainMenuLayout()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 8
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
print(#function)
let padding: CGFloat = 50
let collectionViewSize = collectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainMenuCell", for: indexPath)
cell.backgroundColor = UIColor.red
return cell
}
}
And this is the storyboard...

Conform to UICollectionViewDelegateFlowLayout and implement
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(10, 10, 10, 10)
}
Also comment this line
collectionView.collectionViewLayout = MainMenuLayout()
as no need for a custom layout to accomplish what you want

Related

How to add Footer / Header in CollectionView

code Snippet
class TemporaryViewController: UIViewController {
#IBOutlet weak var itemCollectionView: UICollectionView! // Main Collection View
private var footerView: DashboardBottomViewCollectionViewCell?
override func viewDidLoad() {
super.viewDidLoad()
self.itemCollectionView.delegate = self
self.itemCollectionView.dataSource = self
self.itemCollectionView.register(UINib(nibName: nibParameter.DASHBOARD_CENTER_NIB, bundle: nil), forCellWithReuseIdentifier: nibParameter.DASHBOARD_CENTER_NIB)
self.itemCollectionView.register(UINib(nibName: "DashboardBottomViewCollectionViewCell", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "DashboardBottomViewCollectionViewCell")
}}
extension TemporaryViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
return nil
case UICollectionView.elementKindSectionFooter:
let footerView = itemCollectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "DashboardBottomViewCollectionViewCell", for: indexPath) as! DashboardBottomViewCollectionViewCell
self.footerView = footerView
self.footerView?.contentView?.clipsToBounds = true
footerView?.dashboardBottomDelegate = self
footerView?.clipsToBounds = true
return footerView!
default:
return headerView!
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: 600, height: 300.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: 100.0, height: 300.0)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = -10 + (collectionView.frame.width)
return CGSize(width: width,
height: 90)
}
}
Footer Class to Show collection View
class DashboardBottomViewCollectionViewCell: UICollectionReusableView {
weak var dashboardBottomDelegate: DashboardBottomViewCollectionViewDelegate?
#IBOutlet weak var itemCollectionView: UICollectionView!
#IBOutlet weak var imageView: UIView!
var estimateWidth = 160.0
var cellMarginSize = 16.0
var contentView : DashboardBottomViewCollectionViewCell?
override init(frame: CGRect) {
super.init(frame: frame)
let contents = Bundle.main.loadNibNamed("DashboardBottomViewCollectionViewCell", owner: self, options: nil)?.first as! DashboardBottomViewCollectionViewCell
self.addSubview(contents)
contentView = contents
contents.itemCollectionView.register(UINib(nibName: nibParameter.DASHBOARD_PURCHASE_NIB, bundle: nil), forCellWithReuseIdentifier:nibParameter.DASHBOARD_PURCHASE_NIB)
contents.itemCollectionView.delegate = self
contents.itemCollectionView.dataSource = self
let flow = contents.itemCollectionView?.collectionViewLayout as! UICollectionViewFlowLayout
flow.minimumInteritemSpacing = CGFloat(self.cellMarginSize)
flow.minimumLineSpacing = CGFloat(self.cellMarginSize)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func reloadData() {
contentView?.itemCollectionView.reloadData()
}
}
extension DashboardBottomViewCollectionViewCell: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 15
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: nibParameter.DASHBOARD_PURCHASE_NIB, for: indexPath) as! RecentPurchaseCollectionViewCell
cell.purchaseLabel.text = "trainers.trainerName"
cell.imageView.sd_setImage(with: URL(string: "trainers.tarinerProfilePictureUrl" != nil ? "trainers.tarinerProfilePictureUrl" : ""), placeholderImage: UIImage.init(named: "tour_placeholder"), options: .handleCookies, completed: nil)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = 0 + (collectionView.frame.width / 1)
return CGSize(width: width,
height: 100)
}
}
extension DashboardBottomViewCollectionViewCell : UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
dashboardBottomDelegate?.didSelect(tem: indexPath.item)
}
}
As per the code, I can see this:
I can see a collectionView in Footer but can't change the orientation to vertical instead of the Grid view like this:
I am stuck and couldn't find any hint. Any hint would be appreciable. Thanks
Have you tried putting your setup code DashboardBottomViewCollectionViewCell.init(frame:) in awakeFromNib() instead?
override func awakeFromNib() {
super.awakeFromNib()
//setup cell and stuffs
}
Also call footerView?.reloadData() to reload the contents.

Center Label Based on View

I want to center a label based on a view. The label is inside a collectionView and I want to center this label based on a view that is NOT inside the collectionView at runtime.
What I tried to do is change the constant of the label's constraint to the view's frame midY but it doesn't work:
// THIS DOES NOT WORK. I want the `textLabel` centered inside the `yellowView`.
cell.textLabelCenterYVerticalConstraint.constant = yellowView.frame.midY
Here is the screen:
Here is the Storyboard:
This is my collectionView code
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var yellowView: UIView!
let imageName = ["1", "2", "3"]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 5)
}
}
// MARK: - UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageName.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! MyCollectionViewCell
let index = indexPath.row
let images = imageName[index]
cell.backgroundImageView.image = UIImage(named: images)
// THIS DOES NOT WORK. I want the `textLabel` centered inside the `yellowView`.
cell.textLabelCenterYVerticalConstraint.constant = yellowView.frame.midY
return cell
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.bounds.width, height: collectionView.bounds.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0.0, left: 5.0, bottom: 0.0, right: 5.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
}
This is my cell code
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var textLabelCenterYVerticalConstraint: NSLayoutConstraint!
}
Please see the comment in the code // THIS DOES NOT WORK. Any suggestions?

Nested CollectionView Cell did not appear

I nested collectionView inside collectionView Cell like this:
inside first collectionView Cell, I put this code :
class CaseCellView: UICollectionViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var lblHeader: UILabel!
#IBOutlet weak var collectionViewItemList: UICollectionView!
var sellingArray = ["1", "2", "3", "4", "5"]
override func awakeFromNib(){
super.awakeFromNib()
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.itemSize = CGSize(width: collectionViewItemList.frame.width, height: collectionViewItemList.frame.width)
flowLayout.minimumLineSpacing = 5.0
flowLayout.minimumInteritemSpacing = 5.0
self.collectionViewItemList.collectionViewLayout = flowLayout
self.collectionViewItemList?.delegate = self
self.collectionViewItemList?.dataSource = self
self.collectionViewItemList.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.sellingArray.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionViewItemList.frame.width, height: collectionViewItemList.frame.width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 0, 0, 0)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: "caseListCell", for: indexPath)as! CaseListCellView
cell1.lblItemList.text = self.sellingArray[indexPath.item]
cell1.lblItemList.textColor = .white
cell1.backgroundColor = UIColor.blue
return cell1
}
}
class CaseListCellView: UICollectionViewCell {
#IBOutlet weak var lblItemList: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
but when I run, the nested collectionView result did not appear inside the first collectionView, it just show first lblHeader from the first collectionView Cell.
How to correct my code to show nested collectionView Cell?

Adding header to the tableview with dynamic heigh in swift

I have a UIView(TableHeaderView) which has few labels and collection view. One of the label text is set on run time. So its height is unknown. I'm setting this view as the header for table view.
let tableHeaderView = CommentsTableHeaderView.instanceFromNib() as! CommentsTableHeaderView
commentsTable.tableHeaderView = tableHeaderView
In CommentsTableHeaderview class I have the following code.
I have set postDesc label's number of lines to 0. If the text comes in few lines, its cutting off. I want this to be dynamic and display all text.
class CommentsTableHeaderView: UIView,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
#IBOutlet weak var postDesc: UILabel!
#IBOutlet weak var profileName: UILabel!
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var imageCollectionView: UICollectionView!
#IBOutlet weak var numberOfComments: UILabel!
class func instanceFromNib() -> UIView {
return UINib(nibName: "CommentsTableHeaderView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}
override func awakeFromNib() {
imageCollectionView.delegate = self
imageCollectionView.dataSource = self
imageCollectionView.register(UINib(nibName: "InnerCollectionCell", bundle: nil), forCellWithReuseIdentifier: "InnerCollectionCell")
postDesc.lineBreakMode = .byWordWrapping
postDesc.numberOfLines = 0
postDesc.text = "hi"
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("inside collection view")
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("inside cell")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "InnerCollectionCell", for: indexPath) as! InnerCollectionCell
cell.cellImageView.image = UIImage(named: "Recipe.jpeg")
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
print("inside size\(collectionView.frame.width)")
return CGSize(width: imageCollectionView.frame.width, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets{
return UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
}
}
You need a way to resize your UILabel according to the text size. Do not put height constraints on UILabel. Rather pin its bottom to the bottom view's top.
Do that programmatic resizing after viewDidLoad() - maybe in viewWillAppear() or viewDidAppear(), before you do your first reloadData() call.
Here is a nice tutorial how this should be done - it suggests doing the resizing inside viewDidLayoutSubviews(), which should also be fine.
you need 2 collectionViewDelegate methods:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: x, height: x)
}
if you wish to have a dynamic height in referenceSizeForHeaderInSection height field, set your height according to screen size for ex, UIScreen.main.bounds.height * 0.09

uicollectionviewcell labels misaligned after reload

I tried following code to render simple collection view on screen.
But subviews in contentview do not algin properly
I am coding in swift 2.3 with Xcode 7.3.1
Whys is this happening?
Here is the code
import Foundation
class CustomerOverdueViewController : BaseViewController , UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var lblTotalOverdue: UILabel!
#IBOutlet weak var lblInvestedFunds: UILabel!
#IBOutlet weak var segmentedControl: UISegmentedControl!
var rangeArray = NSArray()
var selectedOverdueIndex = 0
var selectedColor = UIColor()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Customer Overdue"
}
#IBAction func segValueChanged(sender: UISegmentedControl) {
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let reuseIdentifier = String("CustomerOverdueCollectionViewCell")
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomerOverdueCollectionViewCell
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
return CGSize(width: collectionView.frame.size.width/2 - 10, height: collectionView.frame.size.height/3 - 10)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets
{
return UIEdgeInsets(top: 5, left: 5, bottom: 0, right: 5)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat
{
return 10.0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat
{
return 10.0
}
}
Finally I got the solution
Override layoutSubviews in Custom Cell
override func layoutSubviews() {
super.layoutSubviews()
self.layoutIfNeeded()
}
and add following lines in cellForItemAtIndexpath
cell.setNeedsLayout()
cell.layoutIfNeeded()
It seems that in the initial call the data is used when the table is first drawn (from ViewWillLoad). However when you refresh the data, the constraints are not recalculated.
Normally this is done via setNeedsLayout and/or layoutIfNeeded
You can try:
[view layoutIfNeeded];

Resources