I have a parent ViewController which opens a second one.
Second ViewController contains a grid view like in the image.
When I go back and enter same screen again I found layout changed to this
Here in the second image, the UIImage overlaps the label.
Here is my InterfaceBuilder Settings
I change the size and EdgeInsets using following code
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAtIndexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width/2 - 8*2
if let cell = collectionView.cellForItem(at: sizeForItemAtIndexPath) {
return CGSize(width: width, height: cell.frame.height)
} else {
return CGSize(width: width, height: 104)
}
}
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, insetForSectionAtIndex : NSInteger) -> UIEdgeInsets {
return UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8);
}
Why do the first time only the layout is correct and after that the layout changes, Any Clues?
Edit1:
Here is a screen shot for 3d Debugging
Try to give aspect ratio constraint from UILabel to image. Hit and then run.
But before this make sure you did not want a static height for you imageView. If you want to the height of imageView to not be increase or decrease device wise then instead of aspect ratio just give a static height constraint to imageView. Your problem will be solve.
try to set clipToBounds to true on your UIImageView in the xib file
Related
I am working on a project where we use a UICollectionView with a custom cell. The cell looks like this. All of my elements are contained in a UIView.
Of course, the text is much shorter.
Anyway, I decide the width of the CollectionView in the sizeForItemAt function:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width - 50, height: (collectionView.frame.width * 0.7))
}
I need to find a way to keep this fixed width but to make the height of the cell resizable when dynamic type is enabled. The labels at the bottom of the cell must be able to grow in size and therefore resize the cell at the bottom as much as they need without affecting the ImageView's size at the top. However, this seems harder than it looks since my ImageView has a size of 0.7 * the width of my CollectionView (so it has a proper height in any screen).
My labels are contained in a VerticalStackView and my VerticalStackView and the "1" label are contained in a HorizontalStackView.
I have tried to use dynamic type on this kind of view hierarchy in a sample project and the view grows in height without affecting the ImageView but I cannot seem to do the same in a CollectionViewCell.
I tried using auto-layout with Automatic Size enabled for my CollectionViewCell but that just ruins both my width and height and also clips the labels that I cannot see them anymore.
I hope you can help me!
Thank you so much.
You can find your height of the label using text as:
extension String {
func height(withWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(
with: constraintRect,
options: .usesLineFragmentOrigin,
attributes: [NSAttributedString.Key.font: font],
context: nil)
return ceil(boundingBox.height)
}
}
Modify your collection view's sizeForItemAt as:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width - 50.0
let labelHeight = "your text".height(withWidth: width, font: .systemFont(ofSize: 17.0))
let imageHeight = width * 0.7
return CGSize(width: width, height: labelHeight+imageHeight)
}
I have UICollectionView inside UITableViewCell. If I make the size of the CollectionviewCell as custom and provide fixed width then it works fine.
But if I make it automatic and assign size in the delegate then it doesn't work. It takes the default width i.e 50.
Can you please help me out?
Thanks
Code for changing size:
extension BeInspiredTVC : UICollectionViewDelegate , UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width - 50, height: 224)
}
}
I want a full width Collectionviewcell but I am getting like the image below.
take collection view width constraint
next assign self.contentView.frame.width to that constraint in layoutsubsview in tableview cell.
I have set up 2 collectionView in a viewcontroller, both get their data from an endpoint and then reloadData().
One collectionView act like an header tab and have its cell size depend on its intritic size and rely on insetForSection to position/align the cell in the center of the collectionView.
Another have "sort-of" fixed size for themselves where the first cell will be almost the entire width of the collectionView and then the cells after the first one will occupy semi-half the collectionView width.
I have setted-up delegate and extension methods, however for some reason the sizeforItem that focus on the second CollectionView doesn't "stick", they get reverted. When i do :
self.statusOptionCollectionView.reloadData()
self.statusOptionCollectionView.performBatchUpdates({
self.statusOptionCollectionView.layoutIfNeeded()
}) { (complete) in
debugPrint("Batch Update complete")
}
I saw a brief frame of my desired outcome but then the collectionView suddenly undo my sizeForItem code and change the cell to something akin to "size-to-fit". (Pics: Below).
Question is how do i fix this? What is causing this? Is it because i have 2 collectionView in one viewcontroller? I've tried to invalidatingLayout in viewdidlayoutsubviews but it doesn't work. "I did use storyboard but i already delete the collectionView and re-add it, didn't fix it)
I want Something Like This (Focus one the second viewcontroller layout) :
My CollectionView Layout Code is Like This (kindTabCollectionView is the "header", with center alignment) :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
if collectionView.isEqual(self.kindTabCollectionView){
let layout = collectionViewLayout as! UICollectionViewFlowLayout
let totalCellWidth = layout.itemSize.width * CGFloat(self.kindArray.count)
let totalSpacingWidth = CGFloat(8 * (self.kindArray.count - 1))
let leftInset = (collectionView.bounds.size.width - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
let rightInset = leftInset
return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)
}else{
return UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView.isEqual(self.kindTabCollectionView){
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
return flowLayout.itemSize
}else{
let height = CGFloat(40.0)
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let widthMargin = (flowLayout.sectionInset.left + flowLayout.sectionInset.left + flowLayout.minimumInteritemSpacing)
if indexPath.item == 0 && indexPath.section == 0{
let width = floor(collectionView.frame.size.width - widthMargin)
return CGSize(width: width, height: height)
}else{
let width = floor((collectionView.frame.size.width / 2) - widthMargin)
return CGSize(width: width, height: height)
}
}
}
However, the result that come out is this :
(Sorry, it was just a few frame, i tried my best to screen shot it, but it did tried to change to correct frame size, but then it just revert to the small "fitToSize" pic)
Check collectionView's "Estimated Size" attribute in the Size Inspector (Storyboard). It should be set to "None" when using an extension of UICollectionViewDelegateFlowLayout to set cell's size.
As stated in the Xcode 11 Release Notes:
Cells in a UICollectionView can now self size with Auto Layout
constrained views in the canvas. To opt into the behavior for existing
collection views, enable “Automatic” for the collection view’s
estimated size, and “Automatic” for cell’s size from the Size
inspector. If deploying before iOS 13, you can activate self sizing
collection view cells by calling performBatchUpdates(_:completion:)
during viewDidLoad(). (45617083)
So, newly created collectionViews have the attribute "Estimated Size" set as "Automatic" and the cell's size is computed considering its subview dimensions, thus ignoring the UICollectionViewDelegateFlowLayout extension methods, even though they are called.
I have a horizontal collection view.
Each cell has a label and an image.
The problem that I am facing is that when label renders data from the server, sometimes the text is too big and overlaps the image because the width of each cell is fixed.
How can I change the width of the collection View view cell according to the data it contains so that the label does not overlap the image?
CollectionView does have a delegate method which returns cell size.
Please inherit UICollectionViewDelegateFlowLayout and implement the following method.
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 30, height: 30)
}
You can calculate your label width and return it to the function.
Hope this will help.
Calculate the width of the label text first with the font associated with the text.
extension String {
func size(with font: UIFont) -> CGSize {
let fontAttribute = [NSAttributedString.Key.font: font]
let size = self.size(withAttributes: fontAttribute)
return size
}
}
Return the calculated width along with collectionView height in collectionView(_, collectionViewLayout:_, sizeForItemAt)
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Note: Margins are spaces around the cell
// model[indexPath.row].text is the text of label to be shown
let newWidth = model[indexPath.row].text.size(with: labelFont!).width + margins + imageView.width
return CGSize(width: newWidth, height: collectionView.bounds.height)
}
Note:
Don't forgot to confirm your viewController to UICollectionViewDataSource, UICollectionViewDelegateFlowLayout.
In Xcode, I created a UICollectionView and dragged some labels to the UICollectionViewCell. The issue was that when changing devices, the cell won't adjust its size to the screen size. So I implemented the following code:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
return CGSize(width: self.collectionView.frame.width * 0.9, height:self.collectionView.frame.height *0.8)
}
After this, I noticed that the cell's size did change. However, the width and height of the labels inside the cell remained the same.
I'm not sure how to resolve this problem.
Thank you in advance for your help!!
set Constraint of your labels,
like,
for Constraint pic.
your label pic.
or you can also use UI property autoresizingMask
like,
yourLbl.autoresizingMask = [.flexibleWidth, .flexibleHeight]