UICollectionView stick to a cell even if it's content offset may change - ios

This is a hard one, I hope I can explain it properly.
All right, so the thing is that I have a UICollectionView where the height of the cell is dynamically changing using collectionView.collectionViewLayout.invalidateLayout() which guides into the sizeForItemAtIndexPath() method of UICollectionVieFlowLayout.
This works perfectly. There is new content, it resizes, moves the other cells according to it, etc. Now, my collection view's content size is probably pretty big, that means when I'm stopping at cell number, say, 23, and cell number 15 is not even displayed but is changing it's size, it is going to make my cell number 23 move up or down. Can I kind of throw an "anchor" to where I currently am? Here's the code that's resizing the cell, triggered by Firebase:
cell.resizeToText(text: text!)
let size = CGSize(width: self.view.frame.width, height: cell.mainView.frame.height + (cell.mainView.frame.origin.y*2.5))
let invalidationIndex = indexPath.row + 1
self.invalidationSizeDictionary[invalidationIndex] = size
let context = UICollectionViewLayoutInvalidationContext()
context.invalidateItems(at: [indexPath])
self.feedCollectionView.collectionViewLayout.invalidateLayout()
Question: is there a possibility to stay at my current offset position in the collectionView?

Related

Append item horizontally into a tableView cell but make them not fill all the space

I have a specific design that I want to achieve, it doesn't seems to complex to me but I'm struggling a lot to make it happen.
First the design looks like this:
So, I have a table view of items which have a picture, then a title and then a collection of three items which will be ImageViews.
All of this inside a cell and inside a stackView.
I tried to make an horizontal stack view and did manage to append my items correctly but it went horribly wrong in terms of design as my imageviews were stretching all the way horizontally. I guess this is not possible to NOT stretch this items.
I also tried to add a collection view into the table view but figured out that was very complex for a thing this basic (as I guess it should be). And then, I'm here.
Here is my code and where I'm stuck at:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = debateList.dequeueReusableCell(withIdentifier: "itemsBox", for: indexPath) as! ItemBox
// Make a variable with 3 participants
let participants = tableView[indexPath.row].participants?.prefix(3)
// iterate over them and adding them to anything that can be doable
participants?.forEach { participant in
let imageView = UIImageView()
let imageUrl = URL(string: participant.imageURL)
imageView.kf.setImage(with: imageUrl)
// Here add item to something
}
return cell
}
I'm stuck and it's getting too long for something this little. Guess I'm a little bit upset with myself for not figuring out how to do it.
If you are having up to 5 participants, then I would suggest you to add by default 2 subviews in horizontal stack view:
empty view with width constraint >= 5 (call it spaceview)
fixed width for "60 % no" UILabel.
then programmatically insert image views with fixed width and aspect ratio 1:1 in horizontal stack view at index 0.
This way, if there will be only 1-2 participants, then the remaining space will be occupied by spaceview as its width will be greater than 5.
If the participants will be more than 5, then better option is to use collectionview with uilabel in horizontal stack view.
First if they are 3 static number of images then it's better to make them inside design and assign the image urls to their outlets respectively
Second for your current work you need to add a width constraint (regarding height they will fit all the stack height when it's a horizontal stack ) like
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.widthAnchor.constraint(equalToConstant: 50).isActive = true

Self-sizing UICollectionView cells with async image loading

I have a horizontal UICollectionView in which I display images that are loaded asynchronously. The cells are supposed to have their width fit the image.
In viewDidLoad of my view controller, I set the estimated cell size:
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.estimatedItemSize = CGSize(width: 400, height: 400)
In cellForItem, I start the download task using Kingfisher:
cell.imageView.kf.setImage(with: url) { (_, _, _, _) in
cell.layoutSubviews()
}
Inside my cell, I have the following code inside the layoutSubviews method:
// 326 is the image view's height
// .aspectRatio is a custom extension that returns: size.width / size.height
imageViewWidthConstraint.constant = 326 * image.aspectRatio
layoutIfNeeded()
In the storyboard, I have properly setup the layout constraints so that imageViewWidthConstraint is respected for the cell's width.
The following is the result when running my app:
As you can see, the cells have a width of 400, although the image was loaded and the layout updated. And, as result, the images are stretched to fill the image view.
When I scroll to the right & then back, the cells are removed from the collection view and loaded back in, and are now properly laid out:
While scrolling, the cells adjust their width, sometimes to the correct width, sometimes to the wrong one.
What am I doing wrong here?
Since your images come in asynchronously it may take some time to be loaded. Only once they are loaded you can actually know the size or ratio of the image. That means for every image that is loaded you need to "reload" your layout. From a short search this looks promising.
At least this way you may get some animations, otherwise your cells will just keep jumping when images start to be loaded.
In any case I would advise you to avoid this. If I may assume; you are getting some data from server from which you use delivered URLs to download the images and show them on the collection view. The best approach (if possible) is to request that the API is extended so that you receive dimensions of images as well. So instead of
{
id: 1,
image: "https://..."
}
You could have
{
id: 1,
image: {
url: "https://...",
width: 100,
height: 100
}
}
You can now use these values to generate aspect ratio width/height before you download the images.
Next to that I don't really see any good solution for the whole thing to look nice (without jumping around).

UICollectionView ScrollToItem With Insets

I have a horizontally scrolling uicollectionview with insets I am setting through the uicollectionviewdelegateflowlayout method insetForSectionAt.
In trying to use the method .scrollToItem the scroll doesn't seem to take into account of the inset and only scrolls a part of the way.
Is there a suggested approach to account for the inset and have the scroll go all the way? It is just one section and I am trying to scroll from item 0 to item 1.
Another approach is to manually modify content offset based on a custom calculation.
let customPoint = CGPoint(x: customX, y: customY)
collectionView.setContentOffset(customPoint, animated: true)
Where customX and customY are calculated based on cell size and cell count + other factors such as insetForSection.

Different background and collectionCell separator color in UICollectionViewController

I have to create a UI which has 3 UICollectionViewCell in a row. So i created it using UICollectionViewController, with section = 1 and no of rows = coming from api. So i was able to create 6 cells in which every row contains 3 cells. These cells are separated by 0.5 pt vertically and horizontally.
I have given the background color to collectionView which comes in separator.
In particular this is what i need :-
Problem :-
This is the type of UICollectionViewController requirement i have
The separators needs to be red, and the space not rendered needs to be yellow. But I am unable to do that, I can have a single background color for UICollectionView which shows for both separator and free space. Is there a way to have different colors.
I dont want to use UITableView and have a UICollectionView inside UITableViewCell. I know this approach but i want to do the same in UICollectionViewController.
If I understand your question right, you can solve this by removing the space between the cells. Remove the 0.5pt space entirely. Then, all cells will be glued to each other, and you can create your own separators inside each cell. It'll require some logic though, because all cells should not use the same separators (if you do, then the center cells will have double separators on each side).
To achieve your photo, you could set these rules:
The first and the last cell in each row should only show a separator on bottom.
All the other cells in between (in your case, one cell in between) should show separator on bottom, left, and right.
To add these separators, just go into your custom UICollectionViewCell and add some UIViews to each side, and set them to have a width of 0.5pt, and set them visible on demand.
I would suggest a custom cell as Sti suggested. I usually do that. but an easier way would be:
To add a subview to the collectionView every time you reload data of
the collection view.
Code:
let count: CGFloat = CGFloat(items.count)
let numberOfRows: CGFloat = ceil(count / 3.0)
let height: CGFloat = numberOfRows * (cellHeight + seperatorHeight)
if collectionBackView != nil { // controller member object
collectionBackView.removeFromSuperview()
}
collectionBackView = UIView(frame:
CGRect(x: 0, y: 0, width: collectionView.frame.size.width, height: height))
collectionView.addSubview(collectionBackView)
collectionView.sendSubview(toBack: collectionBackView)

Auto layout calculating same height for each collectionViewCell

So I have a collection view that uses a custom layout that I found on Github called CHTCollectionViewWaterfallLayout I like it so far, however I am looking to make the custom collection view cell I made dynamic in height, and I am having trouble connecting Auto Layout to calculate this. I shared a simple sample project on Github that generates random string sizes and displays them, only problem is that Auto Layout generates the same cell height for each Collection View Cell. The project can be found here.
To give you a run down of my thought process, I calculate the cell height by using the method CHTCollectionViewDelegateWaterfallLayout sizeForItemAtIndexPath. Since I also use the delegates method columnCountforSection my thought is since I provide a finite number of columns based on the orientation, I take the collectionView frame width and I divide by the number of columns to get me my width for the cell.
func collectionView (collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
var cell = dict["heightCell"] as? CollectionViewCell
let randomString = RadomStrings[indexPath.item]
let float = CGFloat(columnCount)
let width = collectionView.bounds.size.width / float
if cell == nil {
cell = NSBundle.mainBundle().loadNibNamed("CollectionViewCell", owner: self, options: nil)[0] as? CollectionViewCell
dict["heightCell"] = cell
}
cell?.RandomStringLabel.text = randomString
cell!.frame = CGRectMake(0, 0, CGRectGetWidth(collectionView.bounds), CGRectGetHeight(cell!.frame))
cell!.setNeedsLayout()
cell!.layoutIfNeeded()
var size = cell?.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
size?.width = width
return CGSize(width: width, height: size!.height)
}
My constraints are pretty basic. I have a constraint on each axis. One on the top of the content view, leading, trailing and bottom. I also have a height on the label that is greater than or equal to 45.
Using Auto Layout to calculate TableViewCell heights is easy to me and I like it because I like the DRY principle behind this height calculation approach. So I would like to keep this height calculation process the same throughout my app. CollectionViews are a relatively new layout process for me, so I would love to learn what I am doing wrong here. Hopefully I am clear for everyone, thanks!
I figured it out! What I didn't do is put a width constraint on the custom cell I created! Duh!

Resources