UITableView with fading cells on top and bottom - swift - ios

I need my table view to fade its top and bottom cells as it is scrolled. I have tried some solutions involving gradient and masks but non of it worked, the gradient from clear to white has a black tint. Does anyone has a solution to accomplish that in swift?

You can achieve desired effect by using some methods defined in UITableViewDelegate protocol. First thing you need to know that cell main subview is contentView add all other subviews are subviews of it. What you need to do is to set contentView alpha to 0 in cellForRowAtIndexPath:indexPath: method.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = "Fade Cell"
cell.contentView.alpha = 0 // Here we set the alpha of the content view
return cell
}
If you run your application now, you would have plain white cells. Only thing we need now is to know when cells are displayed, so we can show contentView. Second UITableViewDelegate protocol method comes in handy now.
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
UIView.animateWithDuration(0.4) {
cell.contentView.alpha = 1
}
}
This delegate method is called when cells are preparing to be displayed, and it's the perfect place to animate contentView alpha property to 1.

Related

How to use dynamic height of UITableViewCell in auto-layout and move other views to up when bottom view is hidden?

I have a UITableViewCell in xib and its outlets in corresponding UITableViewCell subclass. I am returning height of cell from
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) ->CGFloat {
return 400
}
I need to hide some views based on the data available in each row of the table and bottom view should shifted to top of the cell. When I am hiding view from cell then there is empty space left in place of hidden view & bottom views are not shifting to top part of the cell.
Here is How I am hiding cell view.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
.....
cell.opetion4.isHidden = true
cell.opetion3.isHidden = true
}
This is my cell.
After hide 2 middle labels it is looking as follows.
But I want to remove this empty space and want to shift bottom label to top as follows.
At first, make the height of UITableViewCell to UITableView.automaticDimension
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Embed all of your questionLabels in a UIStackView (vertical) excluding bottomLabel. Set AutoLayoutConstraint between UIStackView and bottomLabel.
Set the numberOfLines property of UILabels to 0(zero).
Set the Distribution of the UIStackView as Fill
Then, in your tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell method hide the labels. And it will automatically handle the spaces between UILabels
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
cell.questionLabel1.text = labelOneText[indexPath.row]
cell.questionLabel2.text = labelTwoText[indexPath.row]
cell.questionLabel3.text = labelThreeText[indexPath.row]
if labelOneText[indexPath.row] == "" {
cell.questionLabel1.isHidden = true
}
if labelTwoText[indexPath.row] == "" {
cell.questionLabel2.isHidden = true
}
if labelThreeText[indexPath.row] == "" {
cell.questionLabel3.isHidden = true
}
return cell
}
Final Output:
First I suggest you to set UITableViewCell Height to automatic dimensions . Attach all the children to one another and last child to uiview of xib . Now hiding view does not adjust size of cell so you need to play with height constraint of uiview you are hiding .
Make height constraint as strong in IBOutlet else it will crash since cells are re-using and constraint after setting once will become nil . You need to make sure that height constraint are change according to display cell requirement , thats mean for each cell maintain some datasource that decide to show or hide the view every time when cellforrowatIndexpath method called.
Hope this helps
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) ->CGFloat {
return UITableView.automaticDimension
}
now in cell, put all your views and there siblings which you want to hide/show in UIstackview (horizontal). now if you hide one view, it will be hidden and its apace will be also hidden to no white space will be showing, and no need to handle extra constraints. it will all handled by stackview.

I want UITableViewCell to completely cover up the entire screen in ios Swift?

The problem here is I want the UITableViewCell to fully take the view of the cell and once I swipe down the other cell has to appear. What I have here is the other cell also appears on the same UITable screen. As you can see the title of the other news also can be seen in the UITableView. I want the first data to cover up the entire screen and once I swipe down the next data(title,image,description) has to be added.
In viewDidLoad(), make paging enabled:
myTableView.isPagingEnabled = true
then assign height to your cells,
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.myTableView.frame.size.height
}

UIStackView in UITableView cell causes jerking when scrolled

I have a UITableView cell with a stack view inside. When the cell is tapped the data source changes and the table view is reloaded. The stack view will now have more views inside and the cell is bigger. However sometimes when I scroll the table there is jerky behaviour. It's almost like the cell size was calculated wrong or something (even though it looks fine). Once the tableview has jerked once it is fine and doesn't do it again until I tap the cell and it adds more stack views.
I am using UITableViewAutomaticDimension on the table view. I have tried removing the cell and the table doesn't jerk. It's defiantly the stack view causing issues.
I set my estimated row height to as close as possible to the calculated height tableView.estimatedRowHeight = 270. No affect. I have also tried implementing the delegate and it makes no difference. I have tried many combinations or sizes and the result is the same. Any idea on what I am doing wrong here? Do stack views in cells just suck?
I think you are on the right track about estimatedRowHeight causing trouble. I encounter this jerking problem and in pretty much every tableView with varying element size. What usually does the job is "caching" cell heights and returning them in delegate, something like:
class MyViewController: UIViewController {
fileprivate var cachedCellHeights = [IndexPath: CGFloat]()
//your code here
}
extension MyViewController: UITableViewDelegate {
public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cachedCellHeights[indexPath] = cell.frame.height
}
public func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if let height = cachedCellHeights[indexPath] {
return height
}
return 270
}
}
It should work as long as you configure your cell (i.e. add new views to stack view) in tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath).
The same applies to section headers.

Why is cellForRowAtIndexPath being called when the cell is not visible?

I have a UITableViewController sitting inside a Container which sits inside another UIViewController. I built it in a storyboard.
Here is a snap. The UITableViewController in question is on the right-hand side and is called LayerTableViewController.
For some reason, the cellForRowAtIndexPath method of this UITableView is being called for cells which are not currently visible. I tested this by changing the method to the following:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("layerCell", forIndexPath: indexPath) as! LayerTableViewCell
// Configure the cell...
cell.layerCircleView.layer.cornerRadius = cell.layerCircleView.layer.bounds.width / 2
let label = UILabel(frame: CGRect(x: cell.layerCircleView.frame.midX - 10, y: cell.layerCircleView.frame.midY - 10, width: 20, height: 20))
label.text = String(indexPath.row)
cell.layerCircleView.addSubview(label)
print("indexPath.row: \(indexPath.row)")
return cell
}
This code yielded the following odd result. In the console, numbers are being printed occasionally out of order. Cells which are not remotely near the visible screen are having their indexPath.row passed in to this method. Furthermore, the UILabels that I'm making are all being rendered on top of one another.
Here is a combined image of the simulator and terminal output.
As you can see, there is a random line that says indexPath.row: 0 when the indexPath.rows are sitting at around the mid twenties.
So to sum up, why is there a call for cellForRowAtIndexPath with an indexPath.row that isn't currently visible, and why are the numbers rendering on top of one another?
So I'm still not sure as to why there were random calls to cellForRowAtIndexPath for cells that weren't visible, but that doesn't seem to be a big issue. I thought that and the overlaid rendering were connected but they weren't.
The issue with the overlaid rendering is that all the UILabels were being added to a subview of the UITableViewCell, namely cell.layerCircleView. So as I discovered, when the cell is reused, so too is the reference to the layerCircleView. So all I had to do to fix it was clean up the view once it left the screen:
override func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let cell = cell as! LayerTableViewCell
for subview in cell.layerCircleView.subviews {
subview.removeFromSuperview()
}
}

Swift UITableView didSelectRowAtIndexPath bug

I have a UITableView with the subtitles hidden but set up where when someone selects a cell it shows that cell's subtitle. This works fine except that after tapping any cell to reveal its subtitle if you scroll down you will find that every 12 cells have their subtitle unhidden (as well as the one it was supposed to reveal). Here is the code I'm using in didSelectRowAtIndexPath:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
for cell in tableView.visibleCells() {
cell.detailTextLabel??.hidden = true
}
var cell = tableView.cellForRowAtIndexPath(indexPath)
cell?.detailTextLabel?.hidden = false
}
I'm sure this is related to ".visibleCells()" since every 12 cells is about the height of my visible table on my iPhone 6 Plus. When I run it on a 4s in the simulator it's about every 8 cells. But I'm not sure how else to do it besides 'visibleCells'? But it's strange because it's the whole table - all the way down, every 12 cells is showing its subtitle...
thanks for any help
UITableView reuses its cells. So the cell for row a row you clicked on (unhidden the subtitle) may be used for row another row.
The solution is to define prepareForReuse() method in the UITableViewCell subclass (or make the subclass if you do not have one) and hide the subtitle again there.
Add that dataSource's method to your controller. Should work fine.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) {
var identifier = "cellIdentifier"
var cell = tableView. dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
cell.detailTextLabel?.hidden = true
return cell
}

Resources