UICollectionView's Header's height dependent on its content in Swift - swift

Inside a UICollectionView I have a HeaderView within a UILabel. numberOfLines is set to zero to change label's height based on its text. I want the header's height to depend on the label's frame.
PS.
HeaderView in UICollectionView is not as simple as a common view or even UITableViewCell. This feature works with them pretty easy unlike with UICollectionReusableView.

Find the height of your label using this function:-
func labelHeight(width:CGFloat , font:UIFont , text:String)->CGFloat{
let label:UILabel = UILabel.init(frame: CGRect.init(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
add this UICollectionViewDelegateFlowLayout method into your controller and find the height and return header size here
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize{
let height = labelHeight(width: collectionView.bounds.width, font:pass the labelFont here , text:"Pass label text here")
return CGSize(width:collectionView.bounds.width , height: height + 10)
}

Related

How to make tableview row and label height according to label text in swift?

I am making chat related screen, here i have placed view inside tableview cell and label in side view.
for view and i have given leading, trailing, top, bottom
for label i have given leading, trailing, top, bottom constraints
but here cell and label text not coming well, sometime for short text cell becomes big and for long text it remains same like below its coming
here is the code:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
if indexPath.section==0 {
if (indexPath.row%2)==0
{
let cell:ReceiverChatTableViewCell = (chatTableView.dequeueReusableCell(withIdentifier: "ReceiverChatTableViewCell") as! ReceiverChatTableViewCell?)!
let font = UIFont(name: "Helvetica", size: 20.0)
var height = heightForView(text: cell.receiverTextLbl.text!, font: font!, width: 100.0)
if height<60
{
height=60
}
return height
}
}
else
{
return 50
}
return UITableView.automaticDimension
}
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 1000
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
please help me to solve the code.

How to increase the height of header in table view according to the text increasing in label taken in stackview in swift4?

I have taken a UILabel whose content is to be increased whose initial number of lines is 2 then on UIButton click it is increased to 0. Sometimes the height of header increases accurately but otherwise, it increases to a great extent by using viewDidLayoutSubviews
try this it will help you
You can get Label Height using below function
func heightForLabel(text: String, font: UIFont, width: CGFloat) -> CGFloat {
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
UITableView Header Method
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
let height = self.heightForLabel(text: 'your text', font: 'font which you in label' , width: 'your label width')
return height //return height for label.
}

UICollectionView cells lining vertically instead of grid

I am trying to create UICollectionView programatically, and my cells are getting stacked vertically instead of a grid form.
My code is:
let layout:UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
let width = (screenWidth - 4)/3
layout.itemSize = CGSize(width: width, height: width+20)
layout.sectionInset = UIEdgeInsets(top:0,left:0,bottom:0,right:0)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView?.dataSource = self
collectionView?.delegate=self
collectionView?.collectionViewLayout = layout
collectionView?.register(NearbyCollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
self.view.addSubview(collectionView!)
You should always consider collectionView's frame for item size calculation.
In your question you are using UIScreen's width which might get you to wrong calculations because of collectionView's inset properties.
let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
For correct size calculation lets define the number of items in single row.
let maximumItemInRow = 3 (You can play with this as per your requirement)
Now implementing the UICollectionViewDelegateFlowLayout's sizeForItem method.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Getting collectionView's size
var width = collectionView.frame.width
var height = collectionView.frame.height
// Respecting collectionView's ContentInsets properties
width -= (collectionView.contentInset.left + collectionView.contentInset.right)
height -= (collectionView.contentInset.top + collectionView.contentInset.bottom)
if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
// Respecting flowLayout's sectionInset properties
width -= (flowLayout.sectionInset.left + flowLayout.sectionInset.right)
height -= (flowLayout.sectionInset.top + flowLayout.sectionInset.bottom)
// In Vertically scrolling Flow layout
width -= flowLayout.minimumInteritemSpacing
height -= flowLayout.minimumLineSpacing
}
// After considering contentInset and sectionInsets
// we can calculate the width according to
// number of items in single row.
return CGSize(width: width / maximumItemInRow, height: height)
}
You Can use UICollectionViewDelegateFlowLayout as Follows:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let itemWidth: CGFloat = (screenWidth - 4)/3
let itemHeight: CGFloat = itemWidth + 20
return CGSize(width: itemWidth, height: itemHeight)
}

UICollectionView header view like table view header (not the section header)

Is it possible to create UICollectionView header view like UITableView headerView? I mean header view for whole collection view, not the repeated one for each section. Like the picture1 is I want, picture which is now i have done.
I have the solution now. Add a subview in the collectionView and make the collectionView contentInset below the topImageView like below.
topImageView.frame = CGRect(x: 5*SCREEN_SCALE, y: -125*SCREEN_SCALE, width: 285*SCREEN_SCALE, height: 120*SCREEN_SCALE)
collectionView.addSubview(topImageView)
collectionView.contentInset = UIEdgeInsets(top: 130*SCREEN_SCALE, left: 0, bottom: 0, right: 0)
There's a workaround that I use when wanting to achieve this. When setting the header size, I check section number before setting it. If it's the first section, I set the height accordingly - otherwise I set the height to 0 so it isn't visible
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if section == 0 {
return CGSize(width: view.frame.width, height: 35)
} else {
return CGSize(width: view.frame.width, height: 0)
}
}
In swift like below
Register Header View
collectionView.registerClass(HeaderView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerView")
In UICollectionViewDelegate
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
if section == 0 {
let headerView = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "headerView", forIndexPath: indexPath)
headerView.frame.size.height = 100
return headerView }
else {
return nil
}
}
Important is that you are supply the flow layout with the header size
flowLayout.headerReferenceSize = CGSize(width: self.collectionView.frame.width, height: 100)
Otherwise the delegate method will not get called

Add UILabels with different width side by side programmatically

I want to add multiple UILabels programmatically side by side to a TableViewCell. The UILabels have different width.
The first cell in the picture shows the problem and the second cell what I would like to do.
In this example I want to add four UILabels to a TableViewCell. But the width of the TableViewCell is smaller than the width of the UILabels. Therefore I must increase the CellHeight and add the UILabels below to the other UILabels (like the second cell in the picture).
You should put UICollectionView inside of a row of your UITableViewCell.
Each cell of your UICollectionView will have a multi UILabel. Update the dataSource for your UICollectionView depending on your label count. Set isScrollEnabled false of that UICollectionView and set automatic row height for UITableViewCell.
Also, set flow layout to UICollectionView :
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { flowLayout.estimatedItemSize = CGSizeMake(1, 1) }
Adjust cell size like below:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let size: CGSize = keywordArray[indexPath.row].size(attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 14.0)])
return CGSize(width: size.width + 45.0, height: keywordsCollectionView.bounds.size.height)
}
At first you have to make for labels.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
let label1 = UILabel(frame: CGRect(x: 5, y: 5, width: 100, height: 25))
label1.text = "Label 1"
let label2 = UILabel(frame: CGRect(x: 110, y: 5, width: 225, height: 25))
label2.text = "Label 2"
let label3 = UILabel(frame: CGRect(x: 5, y: 40, width: 100, height: 25))
label3.text = "Label 3"
let label4 = UILabel(frame: CGRect(x: 110, y: 40, width: 225, height: 25))
label4.text = "Label 4"
cell.addSubview(label1)
cell.addSubview(label2)
cell.addSubview(label3)
cell.addSubview(label4)
return cell
}
Each label has different width and different position. You can play with it