Only first collectionViewCell reacts to user interactions - ios

The setup
I have a rather complex view hierarchy which made me do terrible things with tableViews and collectionViews. Right now I'm having a regular grouped TableView with custom TableCells. TableCell itself contains a couple of views and a Collection View. The height of the table cell is calculated based on number of items in the data source. On its creation table cell creates a collection view with a calculated size to fit necessary data.
-- UITableView
---- UITableViewCell
------ UICollectionView
--------- UICollectionViewCell
The problem
I've encountered unusual problem with a custom collectionViewCell. I have a vertical single-column collectionView with dynamic amount of cells. Ideally tapping on the cell should call didSelectItemAt. The cell also has three buttons. Tapping on the button should trigger some action. All of the desired functions work only for a first cell. The rest of the cells are not responsive to any actions.
Things that look strange
By default the scrolling of a collectionView inside of the tableViewCell is disabled because it basically fits all the content based on calculated height and doesn't require scrolling. (Also I don't want it to interfere with tableView scrolling logic).
First
I've tried to hardcode some value for the height of collectionView and enable scrolling. What happened is a mystery for me.
Let's say that calculated height required for the collectionView to show all the content without scrolling is 740. When I manually set it to be 280 (this is enough for exactly 2 cells to fit) and enabled scrolling my first cell were still working, but also when I scrolled collection just a little bit my second cell started to act normally as well. When I scrolled back to the top of the collectionView it was disabled once again.
So it looks like when the scrolling is enabled and will actually occur because of insufficient height to fit the content, cells behave as they should. As soon as I set height of the collectionView to be enough to fit its content, things go wrong.
Second
In some cases I can actually tap on the second cell and it will call the delegate. But the weird thing is it works when I tap in the top area of the cell, like 10pts from the top. The other areas of the cell are unresponsive so are the rest of the cells in the collection.
The working delegates and buttons with enabled scrolling forces to think that this has nothing to do with delayed or canceled touches. The frame for collectionView and height of the table cell are calculated properly as well.
xCode 8.3, iOS9+

As it turned out when I was creating my tableViewCell, I hardcoded the height of the collection to value of 214. The reason for that is very simple. I create a cell programmatically and with tableViewCells created programmatically the height of the cell is always 44. When you override init(style: UITableViewCellStyle, reuseIdentifier: String?)
You need to either hardcore the height, or update it in cellForRowAtIndexPath. Even though I updated collection frame when it was filled with data, the container view of this collection wasn't updated resulting in some sort of clipping area for user interactions.

Related

UITableView + UICollectionView + UITableView sizing issue

Below attached image is representing my requirement:
In this image the yellow color UITableView will have list of different UITableViewCell items. In one of the UITableViewCell it has UICollectionView with its list of UICollectionViewCell items and the scroll direction for the UICollectionView is set to horizontal
Each UICollectionViewCell will have one UITableView (Highlighted in Red Color) and it will have its list of UITableViewCell (Highlited in Rose Color) items. Here, the UITableViewCell (Highlited in Rose Color) item can collapse/expand. Whenever the cell is collapse/expand the red table's content size is handled automatically. But, whenever the red table's content size is increased the collectionview size is not increasing.
I have added all relevant constraints to these UI elements. But, the content size or frame of the collectionview is not expanding when a tableview cell is expanded.
To understand better here i am breaking it down how the views are designed:
Yellow tableview embedded inside a UIViewController and it will have one simple UITableViewCell which has no custom class
To the cell programatically the UICollectionView will be added. This UICollectionView is placed in one xib file and it has embeded inside a UIView
UICollectionViewCell has been created in one xib and it will have UITableView element inside.
Also, UITableViewCell has its own xib to refer and this is the cell which will gets expand/collapse
I have tried modifying the intrinsicContentSize property to modify the UICollectionView property's height where it has embedded inside UIView element whenever the expand is happening. Please, refer the CardView.swift for the reference.
CardView.swift
I know the design is bit complex. But, what am i missing here?
Based on your requirement in the image, I just created a sample project here.
I did not use separate Xibs. However, I was able to achieve what you asked for.
You might have to handle the reload of the innermost tableView better based on your apps business logic.
Here's what I have implemented:
The top most view controller has a parent table view.
This table view cell consists of a child collection view.
The child collection view cell consists of a grand child table view.
The grand child table view is the one corresponding to the table view Highlited in Rose Color in the question.
To answer your question about the collection view not expanding: to expand the child collection view when the grandchild table view cell is tapped, I am reloading the parent table view by increasing the row height for the cell containing the collection view. Hence you don't have to worry about adjusting the constraints in auto layout
In case you want to add additional content into the child collection view cell, you can add a height constraint to the grand child table view with a low priority and modify that whenever you need to expand the content in the grand child table view.
if you want to achieve point 6, as suggested by #Ramy Al Zuhouri, you might have to call: collectionView.performBatchUpdates before collectionView.reloadData() to expand the cell size
Refer to the video below for better understanding
This doesn't necessarily solve your problem, but sometimes it's a matter of forcing the collection view to invalidate its layout and recalculating its size. I once received an answer to a TSI from an Apple enginner:
So this is old fashioned way of forcing a re-usable container (Collection || Table) to invalidate its layout. You may even be able to just call invalidate layout. There are oddities in the system with AutoLayout and animations that sometimes throw the system into an odd state. If we do the above the container and can invalidate its layout and make updates without need to re-calculate + incorporate animations. To summarize, calling performBatchUpdates with nil parameters can be useful if you need to force an update based on layout content (not necessarily datasource changes).
It looks like a hack to be used with caution, but it can be worth to give it a try. Whenever you need to recalculate the size of the collection view's cell to expand or shrink, you can do this:
UIView.setAnimationsEnabled(false)
collectionView.performBatchUpdates(nil, completion: nil)
UIView.setAnimationsEnabled(true)
collectionView.reloadData()
This forces a reload, and UIKit is able to recalculate the height of the cell given that you update the height. How? In my case I did by updating a height constraint, but there are multiple ways of doing this. However, it's intended that as the reload happens the collection view should be aware of the new cell height.
the good approach for this, you have to add collectionView in the tableViewHeader
then whenever you scroll up your tableView your collectionView section will also scroll up
to load cells on the specific indexes follow bellow steps:
if(indexpath.row == 0){
//load first tableView cell with collection view
}
else if (indexpath.row == 1){
//load second tableView cell with collection view
}
else{
//load table view cell here
}

Dynamically update uicollectionview cell height

I am working on a quite complicated case
I have a nested UICollectionView
The outer UICollectionView A is for vertical scrolling,
and it's the first cell, is UICollectionView B, for horizontal scrolling.
It has been implemented perfectly, but now there is one more requirement
When scrolling the first cell to the second page, there is one more banner appeared on the bottom of the first cell.
Therefore I need to update the height of cell when scrolling.
If I try to call invalid layout when scrolling, it will be very low efficient. Are there any more methods to support it?
Solved.
First after received the data from the server, each cell height will be calculated and [collectionview reload] will be called.
Then after layouting the collectionview, the size for each cell is cached according to the indexpath.
To implement the 'realtime' resizing, the gesture will call a resizing function directing through a delegate, and directly modify the size cache and call [collectionview.layout invalidlayout].
It looks smooth.

UITableViewCell repeating with scrollview - What workarounds are there?

So, my problem is pretty simple. I have a UITableView in a UIViewController. The tableview has dynamic cells (custom cells with images , text, etc.. from a subclass I created) and these cells belong to a subclass I created. Everything is going fine since the content is different on each cell. The problem is that when I scroll the scrollview of the cell which is horizontal ( I have a UIScrollView in the subview of the cell. I created this scrollview on the cell subclass), on let's say, indexPath.row == 0, and scroll the tableview vertically, after about 8 cells, the ninth cell has the scrollview scrolled as well. This is because of dequeuereusablecells, so the ninth cell is actually the first one, only displaying different content but since it is the first cell, it has the same background operation (the scrollview scrolled).
I tried to unscroll in the cellForRowAtIndexPath: but although this solves the problem it creates another : The first cell that was scrolled is not anymore. So, to solve this new problem I am planning on adding a workaround here that is a dictionary of [Int:Bool], that is, the indexPath corresponding to a Boolean value. If I scrolled the first cell, then 0:true. If I reach the ninth cell (which is equal to the first cell, but with IndexPath = 8 ), I unscroll the cell's horizontal scrollview. If I come back to the beginning of the tableview and reach the first cell, I scroll the cell's horizontal scrollview back. What do you guys think?
The other workaround I can think of is just not use dequeue reusable cells since I don't think I will have more than 30-50 rows on my tableview.
In terms of performance, which operation is better?
Firstly, I would suggest you to carefully analyse whether removing scrolling offset from the first cell is such a bad idea. If I am a user and I scroll down to the very bottom, making the first cell invisible, are you sure that I want it to return to the scrolled position after I scroll back to top? The answer might be Yes, but you should think about this carefully because it might lead you to the simplest solution.
However, in case you really need to do it, I would have a variable which stores the state of the scrolling for each row. If you have an array of objects from which you pull necessary properties (title, image etc) you can add this as an extra property to these objects. Otherwise, you can create an array which will be storing this info (I would prefer an array over dictionary for this task).
Fundamentally it's an issue caused by table view cell reusability. You can have workarounds, but I'd say that, things that aren't supposed to be reused should not be reused.
If your cells are all similar (with minor differences), you could use just one cell identifier; If you have very different types of cells, say, one type has a horizontal scroll view inside, one type just has some labels and images, you might want to consider to have two identifiers, so cells that have UIScrollView won't be reused for normal cells.
And at the same time, you can still do necessary cleanup works in prepareForReuse: to make sure cells that just get dequeued have a fresh start.

UIScrollView in Custom UITableViewCell mixing up data

I have a UIScrollView in each UITableViewCell of my table view that lays out UIViews horizontally - kind of like the "Featured" section on Apple's App Store. When I'm setting up the UITableViewCell I call a function within the custom UITableViewCell to layout the scroll view. This loops through data assigned to that tableview cell's index path and then creates the custom views and adds them to the scroll view. However, these get mixed up when scrolling the tableview and when the tableview refreshes.
If I clear the subviews before laying them out, it does work. However, I'd like to keep the scroll position at the same point every time it shows the cells. How is this possible?
I've just added an external array that stores the current offset for each scrollview, and then manually set the offset on each scrollview everytime the tableview gets refreshed.

UICollectionView within UITableView. How to tell tableview cell to change height when collection view gets taller?

I have a UITableView and one of my table cells is a UICollectionViewController subclass that contains a UICollectionView of displayed email addresses. When a user adds an email the UICollectionView and it’s cell in the table view should get taller.
I’m currently attempting to do this setting my collectionView height constraint to collectionView.contentSize.height in the LayoutSubviews method of my collection controller/cell class. My issue is that the cell in the UITableView is not changing size when this happens.
I am assuming that this is because there is nothing telling the table view that the height of the email entry cell has changed. I am currently using dynamic cell sizing - or trying to anyway. Even, if I call tableView.reloadData() this still does not work. I’m wondering if someone could give me a high-level idea of how they would set this up.
UPDATE:
I had a broken constraint issue that was part of my problem, but this is still not solved. While have proven that I can update the height constraint and that will update the size of the collection view, it's out of sync. It's always one update behind. Here's an image showing the 'UICollectionView' in green and you can see in the logs that I'm updating the constraint (yes, multiple times) each time after adding a new item to the collection, but it is not updating the bounds of the view instance. In this example, if I were to add a new item to the collection, the next time I inspect bounds.height it would be at 149.5. What am I missing!?
i had answered this question in another tableview inside tableview cell that will work for collection view too ,i explained it in this thread
ask me if you faced any problem
It seems you want to set the height of your table view cell dynamically. This detailed tutorial walks you through making a custom cell class that will have dynamic height. Unfortunately, this tutorial only walks you through how to do this with auto layout constraints and a storyboard. In the tutorial you'll notice that the height of the cell depends on the constraints put on a title label within the cell. Try doing the same thing, but applying the constraints on the content in your cell (the collection view cell).
http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout

Resources