I am currently working at a chat app in Xcode 9. I am not using stroyboards doing everything programmatically. For the "chat room" I am using a UICollectionViewController and I want the cells to adjust to the size of the text. I am calculating the size of the cells like that:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = CGSize(width: 250, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimatedFrame = NSString(string: messages[indexPath.item].content as! String).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 18)], context: nil)
return CGSize(width: view.frame.width, height: estimatedFrame.height + 45)
}
So I am basically just calculating the size of the text and then return it as the cells hight. This works fine with short texts, but as soon as the text is longer than 6 lines, the last line is missing. From that point the calculated height is just wrong, because if I just set a constant height, the text shows. Can anyone spot a mistake or should I just use another method to get self sizing chat bubbles in a UICollectionView?
Related
I would like to create a collection view as can be found on Pinterest. It would be 2 columns of pictures.
My problem is really simple. Depending on the pictures used, with my code, a column can contain more size with high height than the other column.
UiviewController:-
func collectionView(collectionView: UICollectionView, heightForPhotoAt indexPath: IndexPath, with width: CGFloat) -> CGFloat {
let image = UIImage(named: slides[indexPath.row].image)
if let height = image?.size.height , let width = image?.size.width{
return height
}
return 0.0
}
I have a UICollectionView with dynamic cell sizing which was working perfectly until I set proportional width constraints on the content inside the cell.
It seems now the UICollectionViewCell is sized to the size of the content, whereas actually I want the opposite - the content needs to be sized proportional to the cell size. Guess I'm missing something...
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = self.collectionView.frame.width
let height = self.view.frame.height/2.5
return CGSize(width: width, height: height)
}
For info, fixed by setting estimated size = none in IB
I'm creating a UICollectionView on my main menu and all the cells look fine bar the last one, where the image is not centered relative to the cell- it appears to be anchored to the top left corner of the cell (not sure about that though).
here is the image that contains the problem.
I didn't really know where to start with this, as all the cells use the same code and constraints.
I checked that it wasn't an image issue by repeating a previously used image (see above screenshot)
My only idea is that the final cell is in a row on its own whereas the other rows have two cells per row.
extension MainMenuViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return mainMenuOptions.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainMenuCell", for: indexPath) as! MainMenuCollectionViewCell
let imageView = cell.mainMenuImage!
let label = cell.mainMenuLabel!
imageView.image = mainMenuImages[indexPath.item]
imageView.frame.size.height = cell.frame.size.width - 5
imageView.frame.size.width = cell.frame.size.width - 10
cell.mainMenuLabel.text = mainMenuOptions[indexPath.item]
let labelText = cell.mainMenuLabel!
label.frame.size.height = Utils.heightForView(text: labelText.text!, font: labelText.font!, width: cell.frame.width)
label.frame.size.width = cell.frame.size.width
label.center.x = cell.frame.size.width / 2
label.center.y = cell.mainMenuImage.frame.size.height + (cell.frame.size.height - cell.mainMenuImage.frame.size.height) / 2
cell.layer.borderColor = UIColor.darkGray.cgColor
cell.layer.borderWidth = CGFloat(0.5)
cell.layer.cornerRadius = CGFloat(10)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainMenuCell", for: indexPath) as! MainMenuCollectionViewCell
let padding: CGFloat = 40
let cellWidth = (collectionView.frame.size.width - padding) / 2
let labelText = mainMenuOptions[indexPath.item]
let cellHeight = cellWidth + Utils.heightForView(text: labelText, font: cell.mainMenuLabel.font!, width: cell.frame.width) + 70
return CGSize(width: cellWidth, height: cellHeight)
}
}
Utils.heightForView() is just a function that calculates the size required for a label to fit all the text. If you need to see it I'll happily add it.
Thanks very much in advance! I hope this is the right amount of code needed but if not let me know and I'll add more.
EDIT: Cell class
class MainMenuCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var mainMenuLabel: UILabel!
#IBOutlet weak var mainMenuImage: UIImageView!
}
When you dequeue a cell in cellForItem it doesn't necessarily have its final size at that point, yet you're depending on that being correct for your code to work.
You should be using autolayout constraints or a stack view inside the cell to give you the correct layout without needing to do any work at dequeue time.
You seem to be trying to give variable heights for your cells as well, which is going to look pretty messy in a flow layout, although it doesn't seem to be making any difference to the cells in the screenshot which I'd expect to have different heights given their titles. Dequeuing a cell in sizeForItem could have some unexpected side effects too, since that's going to mess with the reuse pool.
I am creating a profile page screen for my application. The screen displays all the user's recent posts. I used storyboard to create two UICollectionViewCells in a UICollectionView, one that displays your profile info and the other that displays your posts. See this for how I designed it in storyboard: https://i.stack.imgur.com/6TzwK.png
When I run the application, I get the following result: https://i.stack.imgur.com/XmvY3.jpg
However, I desire the cells placed in a way that it looks like the following: https://i.stack.imgur.com/nWQ14.jpg
How do I force the cells the align so it looks like the image above? Thanks!
Found an answer to the question. Implement UICollectionViewDelegateFlowLayout delegate to your class. Then, include the following method into the class:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = UIScreen.main.bounds.width // get the width of the screen
let scale = (width / 3) // get the width (and height) of each cell
if indexPath.row != 0 { // check to see if the cell is the profile header
return CGSize(width: scale, height: scale) // if not, then return the cell size
}
return CGSize(width: width, height: ((238/414) * width)) // if it is the profile header, return the size for it.
}
Make sure "Min Spacing" for the UICollectionView's storyboard setting to 0,0. The cells will then align into a grid view.
I'm trying to set the UICollectionViewCell height as 90% of the screen size. So the cell size would look good when using different devices.
The problem is that I'm not sure how to set the cell height programmatically. All cell should look the same size, so there's no need to consider the content, I only want the cell height to be 90% of the screen.
Here is what it looks like in iphone 6 screen:
However, when switching to a smaller device it looks like this:
Thank you in advance for your help!
You can set your UICollectionView's flow layout tile size programmatically. There are a couple of different places you could cause this, I would start with viewWillAppear (in your ViewController class) for starters:
override func viewWillAppear() {
super.viewWillAppear()
if let layout = self.schedulesView.collectionViewLayout as? UICollectionViewFlowLayout {
var cellSize = UIScreen.main.bounds.size // start with the full screen size
cellSize.height *= 0.9 // adjust the height by 90%
layout.itemSize = cellSize // set the layouts item size
}
}
You can set collectionView itemSize by using collectionViewFlowLayout according to device size. Try below method..
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
// All the values are changable according to your needs.
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionInset = UIEdgeInsets(top: 25, left: 3, bottom: 3, right: 3)
layout.minimumInteritemSpacing = 50
layout.minimumLineSpacing = 50
return CGSize(width: self.collectionView.frame.width - 50, height:self.collectionView.frame.height - 100)
}
Note: From the above code you will get box type collectionView itemSize that covers the view accordingly to device size.
Just figured what the problem is with contents inside UICollectionViewCell not updating their size.
Just need to as this to the subclass of UICollectionViewCell
override var bounds: CGRect {
didSet {
contentView.frame = bounds
}
}