My goal is to show the yellow tags inline until it fills the available width, then goes to the next line.
I used UICollectionView Flow Layout to achieve that. Here is my full code stripped down to the minimum reproducible example.
What you see on Image 1 is what appears on view did load.
The problem is that the 5th yellow tag could have been placed in the same line as the others because there is enough space. What should I do with my code to achieve the image shown in image 2 ?
Weirdly, after I reload the UICollection view, the layout appears as expected on image 2. You can achieve Image 2 by clicking on the remove button once then add button once.
Along with the fix, I would appreciate a small explanation because I have spent several hours on this issue, tried so many things but nothing works.
This is the code to resize the height of the collection view.
let layoutHeight = collectionView.collectionViewLayout
.collectionViewContentSize.height
collectionViewHeight.constant = layoutHeight
This is the code to size the item cells:
func collectionView(_: UICollectionView,
layout _: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let string = tagList[indexPath.row]
let fontAttribute = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18, weight: .regular)]
let stringSize = string.size(withAttributes: fontAttribute)
let padding: CGFloat = 25
let width = stringSize.width + padding
return CGSize(width: width, height: 50)
}
Image 1 (wrong, the 5th cells should be to the right of the 4th, not below)
Image 2 (Right, expected behaviour, can be achieved but reloading the collection view)
Related
I have a UICollectionView with a cell only contain of one label. I've implemented it working fine, but some string values set to label inside cell cut out. Check below image.
Select cell should display as "Day Before Yesterday". If there's a way to adjust cell width base on data length I can fix this. is it possible ?
PS: some similar questions suggested below method. so I've tried it but no luck.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if(collectionView == dateRangeCollectionView){
return (dateRanges[indexPath.item] as NSString).size(attributes: nil)
}else{
return (OrderStatus[indexPath.item] as NSString).size(attributes: nil)
}
}
I don't think this method even exist in swift 4. when I start to type "sizeForItemAt" Xcode didn't suggest this method.
Please use Self Sizing Cells. Follow the below steps for more informaton
Self sizing cells are only supported with flow layout so make sure thats what you are using.
Follow the following steps :
Set estimatedItemSize on UICollectionViewFlowLayout
Add support for sizing on your cell subclass (Autolayout). More info here.
Checkout this github repo : SelfSizingCollectionViewCell
To calculate the size of each cell, you will need to figure out the width that the text would take, also accounting for the padding given. Use this to get the width of the cell, and return the value in the sizeForItem delegate.
func labelSizeWithString(text: String,fontSize: CGFloat, maxWidth : CGFloat) -> CGRect{
let font = UIFont.systemFontOfSize(fontSize)
let label = UILabel(frame: CGRectMake(0, 0, maxWidth, CGFloat.max))
label.numberOfLines = 0
label.font = font
label.text = text
label.sizeToFit()
return label.frame
}
If you want the text to appear in one or two lines, use the option for numberOfLines.
For the width you get, add in the appropriate padding value.
I need to make UICollectionView cells in oval shape where height is fixed but width is dynamic and it has a limit also, if text longer than that, then text should scroll. Any third party option available for this or need to create own using UICollectionView. Please guide.
Below is the image what i am trying to achieve. I want to know before starting should i look for third parties or use UICollectionView to make own. I have short time to complete that's why to avoid time on searching asking in starting itself which direction to follow.Please guide.
You can use a UICollectionViewFlowLayout and Auto Layout to achieve this.
Create a UICollectionViewCell with a container view.
Pin this container view the edges of the cell with auto layout
Add a UILabel to this container view and pin it to all edges of the container view (give it a background color to distinguish from the cell background)
In the UICollectionViewCell subclass you'll want to round the corners of the container view, e.g. self.containerView.layer.cornerRadius = self.containerView.height / 2
In the UICollectionViewFlowLayoutDelegate method, estimatedSizeForItem return an approximate size for the cell (auto layout will calculate the actual size.)
The important thing to remember is your cell needs to have enough constraints so that the auto layout engine can calculate the actual height and width based on the content.
Edit: If you want a fixed height, ensure your label can only have a single line. Or add a height constraint.
Finally, i found a library TagListView that can be installed through cocoapods with lots of customisation and swift 4 support also.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let nw = intersts[indexPath.row]
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let size = CGSize(width: 250, height: 1500)
let estimatedFrame = NSString(string: nw).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17)], context: nil)
let attributes = [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17)]
let yourLabelSize: CGSize = nw.size(withAttributes:attributes )
var width1 = yourLabelSize.width + 30
if width1 < 30 {
width1 = 30
}
return CGSize(width: estimatedFrame.width+20, height: estimatedFrame.height+20)
}
I just extend or implement #Tim answer. So after you build the cell as described in his answer, then specify the cell width and height to be flexible using the sample code below i.e similar to what he described
let collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.scrollDirection = .horizontal
let itemWidth = Constants.myCollectionViewItemWidth
let itemHeight = Constants.myCollectionViewItemHeight
collectionViewLayout.estimatedItemSize = CGSize(width: itemWidth, height: itemHeight)
myCollectionView.collectionViewLayout = collectionViewLayout
I want a UILabel to appear in the center of my Collection View with a background color. However, when I drag one onto the view in my Storyboard and set its background color it seems to take up the whole view. Instead I want it to just take up a small rectangular section that just surrounds its text within. Here's what I have and what I want it to look like:
Current:
Goal:
Is there any way to set the labels height and width manually? I want it to continue its current behavior of appearing over the cells that get generated in the collection view, but to only take up a limited amount of space instead of taking up the whole view.
EDIT:
Thanks to #robmayoff for commenting that this solution is not possible in case of collection view controller.
This Answer is applicable only if you have a regular view controller but not a collection view controller.
The reason why I'm didn't delete it because -hopefully- it might be useful for some of other viewers.
I suggest to:
Let the label behind the collectionView:
From here (document outline):
Drag the label to be on top of the collectionView (not as a subview). Make sure that both of the components at the same hierarchy, but the label is behind the collectionView. Once you've done this,
Double click on the label (also from the document outline):
Now you are able to move it to the center of the view (by using the arrows on the keyboard for example), make sure to let it be at the center of the screen.
Add the appropriate constraints to let the label to be always in the center of screen.
Make the background of collectionView transperent (clear color).
That's it!
I hope this helps.
Please follow the below steps:
Create an extension of CollectionView
Add the Below function in it.
extension UICollectionView {
func setMessage(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.sizeToFit()
self.backgroundView = messageLabel
}
func removeMessage() {
self.backgroundView = nil
}
}
If you want to remove that message then just use the above removeMessage() function.
Usages
To set message:
self.collcetionview.setMessage("message")
To remove the message:
self.collcetionview.removeEmptyMessage()
Drag a UIView and place it in the collection view to cover it completely.
Drag a UILabel and give its height and width constraint in storyboard and also set constraint for centre in horizontally and vertically.
You also need to set the height and width of collection view in code as shown below
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: cellHeight)
}
I have seen some questions similar to this one but none have really helped me. The last item in my collection view is always lower than the other items, as you can see in the image below:
If I increase the height of the UICollectionView then the last image alligns correctly but there is a huge gap between at the top and bottom of the UICollectionView as seen in the image below:
The sizing of the cells are controlled by this code:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let image = UIImage(data: imageArray[indexPath.row])
var size = image?.size
let height = size!.height
let width = size!.width
let imagewidth = (width / height) * 214
return CGSize(width: imagewidth, height: 214)
}
From what I understand, this should be making the height of each of the images the same, meaning it doesn't matter what the height of the original image is.
I also have another collection view in which the last item is aligned correctly and is set up in the exact same way so I'm a bit confused. What do you think the problem is? I have read that this could be a bug, but is there anyway around it? I am still learning about Swift and programming in general so if you have any other tips for me, they would be massively appreciated.
Thanks
If there is a huge gap between at the top and bottom of the UICollectionView , you can solve the problem via setting viewController's automaticallyAdjustsScrollViewInsets false.
override func viewDidLoad() {
super.viewDidLoad()
//remove the blank of top and bottom in collectionView
self.automaticallyAdjustsScrollViewInsets = false
}
I'm trying to create a collection view with cells that can autosize. previously i had used sizeForItemAtIndexPath but could not get the cell height exactly right for a textView with attributedString. I've decided to abandon that approach and use the auto-sizing feature. I have found some information here but mostly with objective-C. I am only familiar with Swift. Even so, I have picked through it and it is still not working.
What I have done so far is to include the following code in my viewDidLoad:
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(
width: self.view.frame.size.width, height: 100)
}
the cell width is showing correctly however all the cell heights are stuck at 100 regardless of the content.
Is there something else I need to do to allow the autosizing to kick in? I'm pretty sure my storyboard constraints are set up correctly.
In addition to setting the estimatedItemSize, you should also make sure that you are not providing an item size via a delegate or datasource method. The point of the self-sizing mechanism is that UIKit will use the estimated item size as its initial estimate, and then calculate the exact height based on the Auto Layout constraints you've configured on the cell's contentView. Also, you need a Base SDK of iOS 8 or later.
This repo reproduces Apple's example from the WWDC session where they introduced self-sizing cells.
One workaround might be to size the collectionView cells height and width in proportion to the screen size:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let deviceSize = UIScreen.mainScreen().bounds.size
//let cellSize = sqrt(Double(deviceSize.width * deviceSize.height) / (Double(33)))
let cellWidth = ((deviceSize.width / 2) - 10)
let cellHeight = (deviceSize.height / 4)
return CGSize(width: cellWidth , height: cellHeight)
}