Why the cell height is not changing on tapping the button once. It is working only when I tap the button twice - tableview

Click Here To see the UI of my app
Here In the image you can see the ui.
--> There is a tableView & a button on the main view.
--> I have taken a custom tableViewCell class
In the cell I have taken a UIView
Constraints Added To That View are:
centre horizontally, height, width, top & bottom constraint
I have added a outlet of the height constraint to the TVCell class
--> I have taken a button and added a event handler to it
--> Button Event handler:
In button event handler I am just changing the height of the view in the first cell and reloading the cell.
Now the problem is that height of the cell is changing when I tap on the button. But its happening only when I tap one the button twice. So I want to know why is it happening. Here I am adding my code below.
I have one solution for this problem which you can see after you see after the code of the ViewController & TableViewCell Class
VIEW CONTROLLER CLASS
import UIKit
class ViewController: UIViewController,UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.separatorColor = UIColor.black
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
return cell
}
#IBAction func buttonAction(_ sender: Any) {
let indexPath = IndexPath(row: 0, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
if cell.viewHeightConstraint.constant == 75 {
cell.viewHeightConstraint.constant = 10
}
else{
cell.viewHeightConstraint.constant = 75
}
tableView.reloadRows(at: [indexPath] , with: UITableView.RowAnimation.automatic)
}
}
TABLEVIEWCELL CLASS
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var viewHeightConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
//Setting View Height To 75
viewHeightConstraint.constant = 75
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
You can get this source code from this link below
https://github.com/himansudev/tableViewIssue_15_4_20_1.git
Now I have a solution for this problem but I believe its not a proper solution. So what I have done is as shown below
1) I have modified the button event handler, see below is the modified code. Please refer to the above code to identify the changes that I have made to this event handler
#IBAction func buttonAction(_ sender: Any) {
let indexPath = IndexPath(row: 0, section: 0)
tableView.reloadRows(at: [indexPath] , with: UITableView.RowAnimation.automatic)
}
2) Secondly I have added this extra piece of code , see below
override func viewDidLayoutSubviews() {
print("viewDidLayoutSubviews")
let indexPath = IndexPath(row: 0, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
if cell.viewHeightConstraint.constant == 75 {
cell.viewHeightConstraint.constant = 10
}
else{
cell.viewHeightConstraint.constant = 75
}
}
Find the source code after changes below
https://github.com/himansudev/tableViewSolution_15_4_20_1.git
Now after doing these two changes it is working (but sometimes it doesn't work when I tap on the button rapidly many times) but the problem is the viewDidLayoutSubviews() is not getting called but when I comment it than the cell is not working as expected .
SO I want to know why is it behaving that way???

First of all you need to add also the UITableViewDelegate to your ViewController and a var like this:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var cellHeight: CGFloat = 75
and the heightForRow function which will return the right Height:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeight
}
Then on your button action:
#IBAction func buttonAction(_ sender: Any) {
if cellHeight == 75 {
cellHeight = 10
}
else{
cellHeight = 75
}
tableView.reloadData()
}
Also remove the constraint viewHeightConstraint because you do not need it.

Related

Prototype Cell width runs off the page in a Table View

Fairly new to coding and this is my first post here. I added a Prototype Cell to a Table View Cell but when I run the project the cell runs off the page (on the right)? Does anyone have any help they can provide?
This is the ViewController code:
import UIKit
class ConsultationsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
ConsultationFunctions.readConsultations(completion: { [weak self] in
self?.tableView.reloadData()
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Data.consultationModels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ConsutationsTableViewCell
cell.setup(consultationModel: Data.consultationModels[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
And this is the TableViewController code:
import UIKit
class ConsutationsTableViewCell: UITableViewCell {
#IBOutlet weak var cardView: UIView!
#IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
cardView.layer.shadowOffset = CGSize.zero
cardView.layer.shadowColor = UIColor.systemBlue.cgColor
cardView.layer.cornerRadius = 10
}
func setup(consultationModel: ConsultationModel) {
titleLabel.text = consultationModel.title
}
}
I've tried adjusting the Storyboards Identity, Attributes and Size Inspectors as best I can but just don't understand where I've gone wrong? Any guidance is greatly appreciated
I don't see the interface constraints in your code, so I guess you have them in the Storyboard, or ar least you should.
I suggest you to go through some tutorials or guides to "Add New Constraints" for cardView and titleLabel. Here you have a simple one:
https://www.ralfebert.de/ios-examples/uikit/uitableviewcontroller/custom-cells/

iOS 11 reloadSections create duplicate header section

I'm working on a company's project and I have this problem when testing my table view on iOS 11 GM. It did work well on iOS 10. It is simple, I have three sections with header. When I tap on section header, my section will collapse/extend. Here's how I do it:
ViewController:
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var sectionsOpened: [String: Bool] = [
"section1": true,
"section2": true,
"section3": true
]
func isSectionOpened(section: String) -> Bool {
return sectionsOpened[section]!
}
#objc func toggleSection1() {
sectionsOpened["section1"] = !sectionsOpened["section1"]!
toggle(sectionIndex: 0)
}
#objc func toggleSection2() {
sectionsOpened["section2"] = !sectionsOpened["section2"]!
toggle(sectionIndex: 1)
}
#objc func toggleSection3() {
sectionsOpened["section3"] = !sectionsOpened["section3"]!
toggle(sectionIndex: 2)
}
func toggle(sectionIndex: Int) {
self.tableView.reloadSections([sectionIndex], with: .automatic)
self.tableView.scrollToRow(at: IndexPath(row: 0, section: sectionIndex), at: .top, animated: true)
}
Table view dataSource:
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath)
let label = cell.viewWithTag(1) as! UILabel
label.text = "TEST \(indexPath.section) - \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UILabel(frame: CGRect(x: 0, y: 0, width: 320, height: 60))
headerView.backgroundColor = UIColor.green
headerView.text = "Header \(section)"
var gesture: UITapGestureRecognizer?
if section == 0 {
gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection1))
} else if section == 1 {
gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection2))
} else if section == 2 {
gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection3))
}
headerView.addGestureRecognizer(gesture!)
headerView.isUserInteractionEnabled = true
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
Table view delegate:
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if !isSectionOpened(section: "section\(indexPath.section+1)") {
return 1
}
if indexPath.section == 0 {
return 350
} else if indexPath.section == 1 {
return 400
} else if indexPath.section == 2 {
return 350
}
return 500
}
First notice is that scrollToRow behave weird, it go to top, then scroll down to the position.
Then, after trying to open/close the 3 headers, scrolling up/down, sometimes I got this duplicate header problem:
Duplicate header when reloading sections (photo)
'
Thank you in advance for your help. I really need to make this to work because iOS 11 will come next Tuesday...
I had the same problem and fixed it by using:
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
The issue has to do with the estimated row height when using self sizing cells. It seems if we do not provide very accurate estimations a couple of issues may appear across different version of iOS when reloading table view rows. On iOS 9 and 10 the table view may jump towards the top. On iOS 11 it seems section headers may duplicate if they are sticky at the top when reloading a row.
One solution is to cache the hight of the row as it is displayed and then provide this height back to the tableview as the estimated height, when required. Annoying to have to resort to something like this, but it solves both issues for me across iOS 9, 10 and 11.
See also this issue

Xcode 7 - Cell content not displayed into an empty label

I'm trying to display a cell content into an empty UILabel, but after I upgraded to Xcode 7, the content didn't show up. I was following the same mechanism and it worked on Xcode 6, but since I'm new to Swift I may have something wrong in the code.
TableViewController:
import UIKit
class TableViewController: UITableViewController {
struct dataModel {
var name:String
var val:Double
}
let foods = [dataModel(name: "name1", val: 3.3),
dataModel(name: "name2", val: 5.5)]
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return foods.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
// Configure the cell...
let foodCell = foods[indexPath.row]
cell.textLabel?.text = foodCell.name
return cell
}
var valueToPass:String!
var valueInt:Int!
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel!.text
performSegueWithIdentifier("showCalc", sender: self)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showCalc") {
let viewController = segue.destinationViewController as! calcViewController
viewController.passedValue = valueToPass
}
}
}
ViewController where I want to display the tapped cell into an empty label:
import UIKit
class calcViewController: UIViewController {
#IBOutlet var text: UILabel!
#IBOutlet var empty1: UILabel!
var passedValue:String!
override func viewWillAppear(animated: Bool) {
empty1.text = passedValue
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
As a result, there is no error, but there is nothing displayed inside the empty label.
The other thing I want to achieve is to display the Int value into another label and then do some calculations, but this would be another question. Thanks in advance!
There are several issues in this code. First you are obviously missing some closing braces. But I assume this is a copy-paste error.
Why do you use didDeselectRowAtIndexPath? Don't you have a segue from the table view cell to the next controller? Then you should get the text label in prepareForSegue by using tableView.indexPathForSelectedRow.
If you like to keep it this way you should use didSelect... in stead of didDeselect....
You should take the value you want to pass to the next view controller direct from the data and not from the cell. You have the data you present in the table in the foods array.
Class names should always start with a capital letter. ALWAYS!
Having the data model within the controller is poor design. You should have the struct separated in its own file. In this case you could even hand the complete data object to the next view controller.

Swift - Detail View Controller depending on Table View Cell

i have a basic table view controller with dynamic prototypes,
there can appear 4 cells, but there also can appear 6 cells.
What i want is:
Depending on which cell is clicked to switch to another "detail"-ViewController.
my first thought is like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if cell1clicked {
// go to viewcontrollerA
} elseif cell2clicked {
// go to viewcontrollerB
} elseif cell3clicked {
// go to viewcontrollerC
} else {
// go to viewcontrollerD
}
}
is this even possible with tableviewcontrollers?
Greetings and thanks!
Its possible to do it by using TableViewController in storyboard and use static cells instead of dynamic.
Here is the way:
1- Define 4 cells in your static tableView.
2- Drag 4 View Controller in storyboard.
3- Connect First Cell with viewcontrollerA and so on in Storyboard.
4- Be sure that you subclass your class UITableViewController and remove tableview methods.
Update :
If you want to use dynamic cells its possible to push the ViewController programmatically :
1- Drag 4 ViewControllers and go to identity inspector and give each
one a Storyboard ID.
2- Create 4 classes for your ViewControllers and connect each
storyboard with its class.
2- Add Navigation Controller to your initial ViewController in
storyboard.
Ive made you a sample class here :
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource{
var array = ["First","Second","Third","Fourth"]
#IBOutlet weak var tableviewmy: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = array[indexPath.row]
return cell;
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
var controller1 : FirstViewController = storyboard?.instantiateViewControllerWithIdentifier("ViewControllerA") as! FirstViewController
self.navigationController?.pushViewController(controller1, animated: true)
}else if indexPath.row == 1{
var controller2 : SecondViewController = storyboard?.instantiateViewControllerWithIdentifier("ViewControllerB") as! SecondViewController
self.navigationController?.pushViewController(controller2, animated: true)
}else if indexPath.row == 2 {
// controller 3
}else {
// controller 4
}
}
}
You may download a sample project :
https://yadi.sk/d/F5Qb85MLiMPqk

Repeating values in Stepper inside TableViewCell when scrolling

Stepper problem when cells need scroll.
My_TableViewCell.swift
import UIKit
class My_TableViewCell: UITableViewCell {
#IBOutlet weak var My_Label1: UILabel!
#IBOutlet weak var My_Label2: UILabel!
#IBOutlet weak var My_Stepper: UIStepper!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
...
My_TableViewController.swift
import UIKit
var My_Ids = ["C01","C02","C03","C04","C05","C06","C07","C08","C09","C10"]
var My_Values = ["0","0","0","0","0","0","0","0","0","0"]
class My_TableViewController: UITableViewController {
#IBAction func My_Stepper(sender: AnyObject) {
let point = sender.convertPoint(CGPointZero, toView: tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)!
let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! My_TableViewCell
cell.My_Label2.text = "\(Int(cell.My_Stepper.value))"
}
...
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return My_Ids.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell =
self.tableView.dequeueReusableCellWithIdentifier(
"My_TableCell", forIndexPath: indexPath)
as! My_TableViewCell
let row = indexPath.row
cell.My_Label1.text = My_Ids[row]
cell.My_Label2.text = "\(Int(cell.My_Stepper.value))"
return cell
}
...
When all cells fit in a "page" the Stepper works well but if the cells are resized and you need scroll the tableview the values are repeated in other cells.
If you alter the value on the cells C06 and C07 this will to reflect C01 and C02 cells and vice-versa.
Please view this image showing the snapshots with the errors
UITableViewController intentionally reuses cells as an optimization. You need to clear the old cell settings as part of your solution. Add a prepareForReuse method to your My_TableViewCell class.
You do this:
- (void) prepareForReuse
{
[super prepareForReuse];
// clear previous cell settings
}
Additional Notes:
recommend against calling cellForRowAtIndexPath directly. See this thread.
recommend you use Pascal casing for class names: Use MyTableViewCell instead of My_TableViewCell.

Resources