Problem selecting UITableViewCell in Swift - uitableview

I'm new programming in Swift. I have this tableview populated with json data through Alamofire, but I have two problems. I don't know how to add space between cells, the space that I have with my value called "cellSpacingHeight" is an space between sections, but not between cells in the same section.
My other problem is that when I tap a cell, I go to my other View Controller called WorkViewController but always saving the data of the last object of the json data in vc.zoneName = projects.name. What I really need is to tap a cell and go to WorkViewController with the value of the zoneName associated to the cell that I tapped.
I would really appreciate answers with lines of code.

Cell spacing:
I don't think there is a simple way to set cell spacing, what you can do is increase the cell height using the tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat method. For example, if your cell needs to have a height of 100 and a top+bottom margins of 5, you can implement the method like this:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 110
}
Then on your cell ProjectTableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentItem = array_item[indexPath.row]
let dicProject = currentItem as! NSDictionary
let cellProject = projectsTable.dequeueReusableCell(withIdentifier: "cellProject", for: indexPath) as! ProjectTableViewCell
// Add the padding
cellProject.contentView.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0)
ZoneName on cell tap:
Assuming the values presented on the cells are all on the array_item array, you only need to get the array object associated with the cell index and then get the value and assign it to the view controller like this, before presenting the WorkZoneViewController:
let rowItem = array_item[indexPath.row]
vc.zoneName = rowItem.zoneName

Related

How to narrow UITableView cell height if there is dynamic structure in Swift?

I have a tableView and cells. The Cells are loaded from a xib and they have a label with automatic height. I need to narrow one cell if the user taps on it.
I have tried hiding - doesn't work
I have tried removeFromSuperView()- doesn't work
Is there any alternative?
When setting up your tableViewCell store the height anchor you want to update
var yourLabelHeightAnchor: NSLayoutConstraint?
private func setupLayout() {
yourLabelHeightAnchor = yourLabel.heightAnchor.constraint(equalToConstant: 50)
// Deactivate your height anchor as you want first the content to determine the height
yourLabelHeightAnchor?.isActive = false
}
When the user clicks on a cell, notify the tableView that the cell is going to change, and activate the height anchor of your cell.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourTableViewCellIdentifier") as? YourCell
self.tableView.beginUpdates()
cell?.yourLabelHeightAnchor?.isActive = true
self.tableView.endUpdates()
}
Did you try to do something like this:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var result: CGFloat
if (indexPath.row==0) {
result = 50 }
else {result = 130}
return result
}
This is just an example where height is changed for the first row. I tested on my application and it gave result like this.

Is there a way to display a UI Cell a single time and another UI Cell multiple times?

I have a UITableView which represent a questionnaire with Dynamic UI Cells. I've created 2 cells.
The first UI Cell need to be all the time on the top (like a Title/Header) and the second UI Cell need to be Dynamic (this means that the second UI Cell can display 10 or 20 questions). Is there any way to do this ?
I've already created a UITableViewCell for both UI Cells and I linked all the components.
Now I don't know where to specify that Cell 0 to be returned 1 time and Cell 1 to be returned X times.
Also how I keep Cell 0 on the top all the time ?
Here is a printscreen which reflect my issue:
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
// Static cell - Checks Left
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "checksLeftCell", for: indexPath) as! ChecksLeftTableViewCell
return cell
// Dynamic cell - Questions
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.defectAndDamageCell, for: indexPath) as! DefectAndDamageCheckCell
cell.configCell()
cell.delegate = self
return cell
default:
return UITableViewCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0: return 100
case 1: return 400
default:
return 170
}
}
I expect to have "frozen" cell on the top and the second one to be a dynamic cell.
Thanks if you read this and I hope that I explained well what is in my mind.
This isn't sign of good thinking about design. It looks like you don't need UITableView at all. You can achieve better result using basic UIViews for each component such as Title, question details, tags, etc.
You can help yourself using UIStackView.
Instead of a static cell you can use a Section Header with a custom View inside of it:
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let nameLabel = UILabel(frame: CGRect(x: 10, y: 0, width: tabelView.frame.width, height: 40))
nameLabel.text = //
nameLabel.font = taskNameLabel.font.withSize(14)
//Or add whatever you need here
let sub = UIView(frame: CGRect(x: 0, y: 0, width: tabelView.frame.size.width, height: 40))
sub.backgroundColor = .white
sub.addSubview(NameLabel)
return sub
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.defectAndDamageCell, for: indexPath) as! DefectAndDamageCheckCell
cell.configCell()
cell.delegate = self
return cell
}
I would try using UICollectionView instead of the tableview.
From UICollectionView, use the UICollectionViewDelegateFlowLayout delegate.
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { #your code for header view }
https://developer.apple.com/documentation/uikit/uicollectionviewdatasource/1618037-collectionview
or
Create a section header in your TableView, and create a custom view in it.
So your TableView would have 1 section header and 1 row.
Good luck.
There are many ways to solve this.
If you want a static view that sticks to the top try this:
Subclass a UIViewController and add a container UIView at the top/leading/trailing. Embed your header labels and content in the content view. Next, add a UITableView and pin it to the leading/trailing/bottom of the superview. Make your view controller the data source and delegate.
In this way, you will still have scrolling within the tableview and can reload the cells as needed while managing your static header content as IBOutlets.
If you use the selected answer with stack views, you will have to implement a scrolling solution by embedding your stack view in a UIScrollView when your content is longer than the screen size. You should probably just stick to using a tableview because that behavior is inherited.

Why in heightForRowAt method my cell is nil?

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let cell = tableView.cellForRow(at: indexPath) as? ExpandablePlaneTableViewCell {
return 400.0
}
return 75.0
}
I want to change size of my cell but inside of heightForRowAt it cannot see my cell and crashed. When I put there if let check it does not enter inside of the block and just takes 75.
Can anyone tell me what the problem is? It's too strange for me!
I already set delegate to self. So it call the function but cannot detect my cell there.
UPDATE
In my ExpandablePlaneTableViewCell I have a variable:
var exapanded = false
Later in my ViewController: On click on the button in the cell I run my delegate method:
func expandViewButtonTapped(_ sender: ExpandablePlaneTableViewCell, for indexPath: IndexPath) {
sender.exapanded = true
tableView.reloadRows(at: [indexPath], with: .automatic)
}
and after I want to expand it and reload the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "expandableCell", for: indexPath) as! ExpandablePlaneTableViewCell
cell.delegate = self
cell.indexPath = indexPath
return cell
}
Do not attempt to get a cell in heightForRowAt. And there certainly is no reason to do so in your case.
You seem to want the height to be one value for certain types of cells and another height for other types.
Simply use the same basic logic you have in cellForRowAt, based on the indexPath, to determine which height to return. In other words, base the decision on your data model, not on the cell.

Hide individual row if array is empty for that row

I am creating a statistics page that displays the number of times an item is used.
Each row has an itemTitle and itemCount.
Not every itemTitle have data (if user does not populate that data, but every row will have itemCount).
I want to hide the empty rows.
Here is what I am trying, but when this is true it hides all rows, not just the individual empty rows.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "StatsCell", for: indexPath)
let itemTitle: UILabel = cell.viewWithTag(1000) as! UILabel
let itemCount: UILabel = cell.viewWithTag(1001) as! UILabel
itemTitle.text = self.statsArray[(indexPath as NSIndexPath).row].itemTitle
itemCount.text = "\(self.statsArray[(indexPath as NSIndexPath).row].itemCount)"
if (self.statsArray[(indexPath as NSIndexPath).row].itemTitle).isEmpty {
tableView.rowHeight = 0
}
return cell
}
See the attached example
Thanks
You should implement override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat in your code and return the height as 0 for the row which has an empty array in the datasource. Return the normal height otherwise.

UITableViewCells invisible

Requirement :
I have a list of UITableviewCell loaded from a nib that I'm presenting on UITableview. The first time I open the UIViewController all cells are shown correctly and work as expected.
Issue :
If I navigate back to the parent and then open the UIViewController again the UITableviewCell are 'invisible'. I say invisible because with a breakpoint in cellForRowAt I can see that the table view does load all cells and the cells are valid.
Code :
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 13
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = (project?.sliderData.sliders[indexPath.row].view)! as UITableViewCell
print(cell.contentView.subviews.count)
if let left = cell.viewWithTag(2) as? UILabel {
left.text = "left"
}
if let middle = cell.viewWithTag(3) as? UILabel {
middle.text = "middle"
}
if let right = cell.viewWithTag(4) as? UILabel {
right.text = "right"
}
return cell
}
Screen Shot Image
Expected observation :
I was thinking that maybe the subviews of the cells get released because I don't have any bindings to them in IB. To test this I'm printing the count of subviews and writing some text to the subview labels. And everything seems to go fine, the cells are loaded and the labels are there but the cells just don't show up.
But then, if I scroll the TableView up and down a little to get some cells updated those cells do appear at the top and bottom of the view as shown in the pic.
You need to call dequeueReusableCell(withIdentifier: "cell") inside your code then will show your table cell. It will reuse cell for your all numbers of row data content.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
return cell
}
More Details : How to create uitableview with multiple sections in iOS Swift.
Did not find reason why the tableView behaves the way it does so I solved the issue by dequeueing default cells. The views provided by the slider objects are added as subviews to the dequeued cells. Now the subviews can of course be any UIViews.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "sliderCell")
if cell == nil {
cell = UITableViewCell.init(style: .default, reuseIdentifier: "sliderCell")
}
cell?.addSubview((project?.sliderData.sliders[indexPath.row].view)!)
return cell!
}

Resources