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
Related
Am writing this question just as a reference and to help anyone facing the same issue I faced. Many answers here at SO and in google search didn't provide a complete solution. So Here I answer my own question
Here is the code that works and sets both width and height correctly
import UIKit
class MyViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
#IBOutlet weak var myCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.delegate = self
myCollectionView.reloadData()
}
//Other methods here
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) //Put your Cell Identifier istead of Cell
let height = cell.contentView.frame.height
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return .zero
}
}
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?
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?
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
I got two questions.
I am wondering why my collection view automatically load data without calling imageCollectionView.reloadData().
Solved. See comment
Why func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize is not called? I didn't see the print("collectionViewLayout called") I am trying to modify the cell's size so the cell's height equals to the collection view height
Solved. See comment
Here is the codes
class ProductInternalDetailVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var selectedProduct: Product?
#IBOutlet weak var imageCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
imageCollectionView.delegate = self
imageCollectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! ProductInternalDetailCVC
if indexPath.row == 0 {
cell.productImage.image = selectedProduct!.image1
} else {
cell.productImage.image = selectedProduct!.image2
}
cell.productImage.frame = CGRect(x: 1, y: 1, width: cell.frame.size.width-2, height: cell.frame.size.height-2)
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
//return CGSize(width: collectionView.frame.size.height-1, height: collectionView.frame.size.height-1)
print("collectionViewLayout called")
return CGSize(width: 10, height: 10)
}
}
class ProductInternalDetailCVC: UICollectionViewCell {
#IBOutlet weak var productImage: UIImageView!
}
Thanks for the help.
Question 2: The function signature has changed a bit. Change the function signature with this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
...
}
And don't forget to implement the UICollectionViewDelegateFlowLayout protocol.
Question 1:
When entering the ViewController your collectionView loads the datasource of the collectionView automatically if this is your question.for example, make changes on datasource without changing the view (means without calling viewDidLoad method) you will not see the changes until you call imageCollectionView.reloadData().