Sorry for the influx of UI and GUI questions but I was recently looking at an application called Ganna and in this pic below I was wondering how you would get that random Ganna Around The Globe banner beneath the Recently Played horizontal collection view and above the Top Charts horizontal collection view.
The way it's organized is that there is a main collection view with multiple horizontal collection views such as the Recently Played and Top Charts horizontal collection view.
I am thinking if the indexPath is 0 (which means you are at the Recently Played horizontal collection view) then you would insert that Ganna Around the Globe banner underneath that collection view. But this would be difficult since I would have to return different heights of the cell depending on whether that banner exists or not. And this banner appears 3 times beneath 3 different horizontal collection views.
Do you guys have any ideas? Thank you!
I can't say if this is how that particular example is implemented. But the way I would approach this is fairly simple.
You would have a single TableView with dynamic cells that covers the entire screen from nav bar to tab bar. Make two custom cells, the first custom cell contains a collectionView. The second cell would be laid out in whatever manner you want for displaying that banner.
In your cellForRow method of the tableViewDelegate you set the delegate of the collectionView inside the cell to whatever it needs to be.
The only thing left after that is a tiny piece of logic in the heightForRow method. Something like
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (indexPath.row == kBannerCellIndex) {
return showBanner ? kBannerHeight : 0
}
// the rest of any height logic
return defaultCellHeight
}
Related
I know there are tons of questions about tableViewCell disappearing on scroll, but none of those fit into my situation.
I say disappear, I mean the whole cell disappears, not some of its subviews.
Here's my situation:
I have a tableView with custom cells, each one of them is very complex, some could even host up to three collectionViews in them. Most of these cells are only displayed once, so I cache them. I retrieve data from network, and use it to populate the cells. As sometimes the data may not be fully available, some sections of the cell needs to be hidden: the cell will set some of its collectionViews' height constraint to zero, and isHidden to true, then notify the tableView, who will reload the cells' heights using:
tableView.beginUpdate()
tableView.endUpdate()
I have noticed two behaviors:
1:
If none of the cell's portion is hidden, the cell works perfectly, except when I scroll(from bottom up) too fast, the cell disappears before going out of the bound of the tableView. When I scroll slow, this does not happen.
2:
If some part of the cell is hidden, the cell will disappear before going out of tableView EVEN WHEN I scroll very slowly.
Sorry I cannot post any code, but what I might have done wrong?
P.S.: In my tableView's row height delegate method, I have:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath.row] ?? 0.1
}
cellHeights is where I use a dictionary to store computed height of the corresponding cell. I am certain that these height values are correct, because when I scroll from top down, the cell looks all right. This only happens when I scroll from bottom up.
I did some experiment with the default height 0.1. When I changed it to 1000, the cells are disappearing randomly even more. 0.1 looks relatively stable, but the problems above still exist.
Thanks.
I have read a few posts asking about to put UITableViews inside a ScrollView but mostly this idea is not recommended. The "standard way" to handle this situation as I understand is to use just the UITableView instead of the ScrollView and make the TableView header the UIView to display the content and to use the cell to display any dynamic rows.
However the app I am now developing is a bit different: the first part of the view is some descriptive content that I can use the header view to display. But after that I want to display two scrollable sections potentially with dynamic height. One is a comment section displaying all comments and replies and another is a user section that display any users ever made an offer. This two sections need to be row-based and will be dynamic in height as the content inside them will be different.
So the question is, how should I implement this without putting two UITableViews inside a UIScrollView(My original plan)?
UITableViews inside a ScrollView is not at all recommended as UITableView as its own scrollView as subView.
This two sections need to be row-based and will be dynamic in height
as the content inside them will be different.
Whats wrong with using the delegate for dynamic height for row
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {}
Some Tutorial:
https://www.raywenderlich.com/129059/self-sizing-table-view-cells
Update: It looks like the tableView.estimatedRowHeight setting is somehow related to the problem, since the variance of the problem changes as the number changes, and setting it to 9999 makes it "go away" but that seems super hacky. The 9999 also breaks the animations I intended to do on the show/hide. 9999 also breaks the scrollbars funtionality. See the discussion thread below for additional details and full context.
I have a view controller which holds two Container Views. Each container view holds a UITableView controller. They're orientated vertically each taking up the full height.
The container on the right side has a width of 100 and starts off screen with the trailing constraint set to -100 which allows the other container to fill the whole screen initially.
The table on the left has automatic adjusting cell heights using UITableViewAutomaticDimension since when the hidden view is shown, some text on two lines may need to wrap to the next line.
When I change the constraint to be 0 and show the view, if I'm not at the top of the list, rather than expanding what's visible on the screen, it seems to be expanding everything, thus pushing what was on the screen down, further than it should be going.
What's weird though, is subsequent expand/collapses work as expected, only adjusting what is visible.
I've attached a video a video showing the issue, I'm just trying to connect things up and get the sizing working right now, the content in the right view will be different eventually.
The following is what's happening in the video
I expand/collapse from the top of the list and it works as expected
I scroll to middle of list, and expand, and my content gets pushed down further than it should be.
I then collapse and expand it again, and this time it stays in place since no sizes changed on the visible screen.
It doesn't matter how far down the list I go, the same behaviour happens, getting worse as I go further down.
I'm adjusting the constraints like this on a button press for now.
if self.timeContainerConstraint.constant == 0 {
self.timeContainerConstraint.constant = -90.0
} else {
self.timeContainerConstraint.constant = 0.0
}
Does anyone know why the content is jumping when I scroll down the list and expand the first time, but not jumping on subsequent attempts?
As you have variable cell heights, the tableview can't correctly calculate the scroll offset when its frame changes. It has to rely on the estimated row height, but as you have a fixed value for this, the more cells 'above' the current scroll position, the greater the error between the estimated and the actual value.
I was able to solve this by caching the cell heights in order to return a more accurate estimated height using estimatedHeightForRowAt:
var cellHeights = [IndexPath:CGFloat]()
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let frame = tableView.rectForRow(at: indexPath)
self.cellHeights[indexPath] = frame.size.height
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return self.cellHeights[indexPath] ?? 140.0
}
Using this approach, the expansion and contraction of the side bar didn't affect the table's position. I was able to animate the constraint change for a nice smooth reveal and the table didn't 'jump', even if the table was scrolling while the side bar was opened.
My sample project is here
Apologies in advance if this has been answered somewhere. I've looked everywhere and I'm still unsure what to do. (And answers that use Objective C are almost completely worthless to me.) I'm somewhat new to iOS.
I have a UITableView which serves as a newsfeed of sorts, displaying a series of posts. (a la a twitter newsfeed, for instance.) The cells in this table view are (currently) derived from a single cell prototype, created in xcode's interface builder. Each cell contains subviews to display things like username, profile image thumbnail, title, message, date, location, another image, etc.
The problem is, depending on what data a particular post contains, many of these subviews either should or should not be shown -- if a post doesn't contain an image, then that cell's image view should not be shown; if a post doesn't have a date and/or location, one or both of these views should not be shown. Not only should the unused fields be empty, but they shouldn't take up any space in the cell.
I read in Using Auto Layout in UITableView for dynamic cell layouts & variable row heights (under "2. Determine Unique Table View Cell Reuse Identifiers". Wonderful answer by #smileyborg, btw.) that for each different layout of subviews that could be in a cell, a different prototype cell and reuse identifier should be used. But this would require me to have a cell prototype for every single possible combination of data items in a post, even if the difference is single label! Surely there must be a better way.
What is the safe and correct way to do what I need to do? Is there perhaps a way to remove subviews from cells at runtime (and have the layout adjust its spacing accordingly) without completely screwing up cell recycling?
Ill assume you know everything you want to know about your layout when you see the cell.
So its basic tableview cell layout. Dequeue and decorate.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(MyIdentifier, forIndexPath: indexPath) as! MyCustomCellClass
let data:MyDataClass = myDataAtIndexPath(indexPath)
decorateCell(cell,data:data)
return cell
}
func decorateCell(cell:MyCustomCellClass,data:MyDataClass) {
//here is where you arrange/change your constraints and hide/reveal views
//depending on the data
}
but also what you need is to allow the cell to set its height properly
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return someEstimatedNumberWhichIsClose;
}
but most crucially in your XIB, the cell MUST have a continuous line of constraints from top to bottom which describe the height.
So this cell will auto-set its height correctly.
While this one will not.
NB - in the upper example , if you leave the height constraint off the text-field and make it infinite line count the cell will grow/shrink appropriately.
So to summarise you can use one multipurpose cell as long as you keep your height constraints coherent.
As a general rule - Swiss Army Cells® which do lots of things may get a bit unwieldy so you do need think about how many use cases you wish to support with one cell and then maybe start creating multiple types/identifiers.
In short: how can I prevent the selection of a collection view cell from affecting the selection state of table view cells that are inside that collection view cell?
I have a UICollectionView, where the cells are displayed full-screen when selected. The cells contain a UITableView that will display use information.
The problem I'm having is that the cells in the table view are being displayed as if they are selected. I moved some things around, to display a cell full-screen all the time, which allowed me to confirm: when the collection view cell is selected, the cells of the table view inside of it are also displayed with the selected style.
Note that I say they are displayed with the selected style, and not they are selected. As far as the table view is concerned, they are not selected.
[tableView indexPathsForSelectedRows]; returns no index paths. tableView:willSelectRowAtIndexPath:—from which I could return nil—is never called. So, it's safe to say that the table view doesn't perceive its own cells as being selected.
But, I can confirm in Reveal that the table view cells contain the selected background view (see the last screenshot below). This is only the case for table view cells that are visible when the collection view cell is selected: this does not happen to cells that are far enough down in the list to be off-screen initially.
I can hide the problem using cell.selectionStyle = UITableViewCellSelectionStyleNone; and ultimately, this might be my solution: the table view cells will not be selectable anyway, so painting over the problem like this should work in my situation.
But, that doesn't answer the bigger question: what the heck is going on here?
Screenshots
Normal collection view cell (this whole thing is one cell):
Selected collection view cell, with gray selected style on the table view within:
Reveal, showing the selected background view. In this case, the table cell containing the Delete button was not visible when the collection view cell was selected, so it didn't get the selection styling like the cells above it did:
I've overridden UITableViewCell's method this way
func setHighlighted(_ highlighted: Bool, animated: Bool)
{
//Leave Empty
}
Apple behaviour could be right, because in this way you could skip an iteration on all child and set manually the property, but why I can't choice if i wan't it using UICollectionViewDelegate method?
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool
Apparently, this is the expected behavior in iOS 7. setHighlighted: is called on all subviews that support it.