So i am trying to get the value of the textLabel of the row I select. I tried printing it, but it didn't work. After some research I found out that this code worked, but only in Objective-C;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"did select and the text is %#",[tableView cellForRowAtIndexPath:indexPath].textLabel.text);]
}
I could not find any solution for Swift. Printing the indexpath.row is possible though, but that is not what I need.
so what should I do? or what is the 'Swift-version' of this code?
Try this:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow() //optional, to get from any UIButton for example
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell
print(currentCell.textLabel!.text)
If you're in a class inherited from UITableViewController, then this is the swift version:
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let cell = self.tableView.cellForRowAtIndexPath(indexPath)
NSLog("did select and the text is \(cell?.textLabel?.text)")
}
Note that cell is an optional, so it must be unwrapped - and the same for textLabel. If any of the 2 is nil (unlikely to happen, because the method is called with a valid index path), if you want to be sure that a valid value is printed, then you should check that both cell and textLabel are both not nil:
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let cell = self.tableView.cellForRowAtIndexPath(indexPath)
let text = cell?.textLabel?.text
if let text = text {
NSLog("did select and the text is \(text)")
}
}
Swift 4
To get the label of the selected row:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
print(cell.textLabel?.text)
}
To get the label of the deselected row:
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
print(cell.textLabel?.text)
}
If you want to print the text of a UITableViewCell according to its matching NSIndexPath, you have to use UITableViewDelegate's tableView:didSelectRowAtIndexPath: method and get a reference of the selected UITableViewCell with UITableView's cellForRowAtIndexPath: method.
For example:
import UIKit
class TableViewController: UITableViewController {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
switch indexPath.row {
case 0: cell.textLabel?.text = "Bike"
case 1: cell.textLabel?.text = "Car"
case 2: cell.textLabel?.text = "Ball"
default: cell.textLabel?.text = "Boat"
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)
print(selectedCell?.textLabel?.text)
// this will print Optional("Bike") if indexPath.row == 0
}
}
However, for many reasons, I would not encourage you to use the previous code. Your UITableViewCell should only be responsible for displaying some content given by a model. In most cases, what you want is to print the content of your model (could be an Array of String) according to a NSIndexPath. By doing things like this, you will separate each element's responsibilities.
Thereby, this is what I would recommend:
import UIKit
class TableViewController: UITableViewController {
let toysArray = ["Bike", "Car", "Ball", "Boat"]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return toysArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = toysArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let toy = toysArray[indexPath.row]
print(toy)
// this will print "Bike" if indexPath.row == 0
}
}
As you can see, with this code, you don't have to deal with optionals and don't even need to get a reference of the matching UITableViewCell inside tableView:didSelectRowAtIndexPath: in order to print the desired text.
In my case I made small changes, when i search the value in tabelview select (didSelectRowAtIndexPath) the cell its return the index of the cell so im get problem in move one viewControler to another.By using this method i found a solution to redirect to a new viewControler
let indexPath = tableView.indexPathForSelectedRow!
let currentCellValue = tableView.cellForRow(at: indexPath!)! as UITableViewCell
let textLabelText = currentCellValue.textLabel!.text
print(textLabelText)
In swift 4 :
by overriding method
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name : "Main", bundle: nil)
let next vc = storyboard.instantiateViewController(withIdentifier: "nextvcIdentifier") as! NextViewController
self.navigationController?.pushViewController(prayerVC, animated: true)
}
This will work:
let item = tableView.cellForRowAtIndexPath(indexPath)!.textLabel!.text!
Maintain an array which stores data in the cellforindexPath method itself :-
[arryname objectAtIndex:indexPath.row];
Using same code in the didselectaAtIndexPath method too.. Good luck :)
Related
I take tableView and two different cell.xib files , I want to display when i click cell1 then i should display cell2 data.
class TableView: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var array1 = ["Click1","Click2"]
var array2 = [[ "one","two","Three"],["Four","Five"]]
var selectedArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "MainCell", bundle: nil) , forCellReuseIdentifier: "MainCell")//This is used to add xib file with identifier
tableView.register(UINib(nibName: "SecondCell", bundle: nil) , forCellReuseIdentifier: "SecondCell")
}
//MARK:DataSource Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array1.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "MainCell") as! MainCell
cell.textLabel?.text = array1[indexPath.row]
return cell
}
//MARK: tableViewDelegate Method
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "SecondCell") as! SecondCell
selectedArray = array2[indexPath.row]
cell.textLabel?.text = selectedArray[indexPath.row]
}
}
Tell how can i do that if i press first cell it should show 2nd cell values as per indexPath.row
I think you want to achieve expandable cells. You can use the header cell for this one.
You might want to read this:
Hope this helps!
For example, you can add a flag which indicates whether the first cell was tapped.
var wasFirstCellTapped = false
Then numberOfRowsInSection depends on this flag:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return wasFirstCellTapped ? array1.count : 1
}
In didSelectRowAt:indexPath set this flag to true. And perform tableView changes. The simplest way is to call tableView.reloadData(). But you can animate this using insertRowsAtIndexPaths
var allCellsArray = ["Click1","Click2"]
var displayingCellsArray = ["Click1"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return displayingCellsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell!
if displayingCellsArray[indexPath.row] == "Click1" {
cell = self.tableView.dequeueReusableCell(withIdentifier: "MainCell") as! MainCell
}else {
cell = self.tableView.dequeueReusableCell(withIdentifier: "SecondCell") as! SecondCell
}
cell.textLabel?.text = displayingCellsArray[indexPath.row]
return cell
}
//MARK: tableViewDelegate Method
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let data = displayingCellsArray[indexPath.row]
//Below logic will show (Cell2 if Cell1 is clicked and hide Cell1)
// (and show Cell1 if clicked Cell2 and hide Cell2)
if data == "Click1" {
displayingCellsArray = [allCellsArray.last!]
}else {
displayingCellsArray = [allCellsArray.first!]
}
tableView.reloadData()
}
You should handle such logic in a separate datasource array by adding/removing that sort of data from this array which shows particular cells.
E.g
In your case you have two values in array Click1 & Click2
Click1 shows MainCell & Click2 shows SecondCell
So first add Click1 in your array and when this cell is tapped simply add Click2 in your array and reload. If you want to remove MainCell when SecondCell is displaying then while adding Click2 simply remove Click1
You can expand the second cell on click of first cell..
Refer this example to expand and collapse the cells
https://github.com/jonasman/JNExpandableTableView
I have struggled with this problem for AEONS, and I thought I finally came across the solution. But no. I am trying to make a tableview where the images are loaded into the correct cells and persisted, without flickering.
Here is my solution:
extension SearchViewController : BookDataProcessorDelegate {
func didFetchSmall(image: UIImage?, for indexPath: IndexPath, in tableView: UITableView) {
if tableView == resultsTableView {
if let cell = tableView.cellForRow(at: indexPath) as? BookTableViewCell {
cell.imageView?.image = image
}
}
}
func bookSearchDidFinish(with books: [Book]?) {
self.books = books == nil ? [] : Array(books!.prefix(MAX_BOOK_COUNT))
resultsTableView.reloadData()
}
}
extension SearchViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.books.count
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
BookDataProcessor.shared.fetchSmallImage(for: books[indexPath.row], at:indexPath, in:resultsTableView)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bookCell", for: indexPath) as! BookTableViewCell
cell.authorLabel.text = books[indexPath.row].authorName?[0] ?? ""
cell.titleLabel.text = books[indexPath.row].title ?? ""
cell.delegate = self
cell.indexPath = indexPath
return cell
}
It is pretty self explanatory. Basically, the BookDataProcessor.shared.fetchSmallImage triggers the didFetchSmall method, which should set the correct tableView cell. However, the cells flicker and disappear half the time. I am extremely interested to know what I am doing wrong.
I'm writing an app that have a dashboard with multiple cells. One of the cells have a question, but the answer are dynamically filled, so I decided to use a UITableView to handle it.
I set the the UITableViewCell as the delegate and dataSource of the internal UITableView and made the configurations for define the cell and the selected state.
extension SurveyTableViewCell: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
answers = model.getSurveyAnswers()
return (answers?.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.idAnswer.text = alphabetQuestion[indexPath.row]
cell.answer.text = answers?[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.selectRow()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
But the click inside the cell in the internal UITableViewCell is not recognized. I need to recognize this click to after send the user answer to the server.
I saw some solutions, but using storyboard. I use only nib's on my projects.
But I still tried with an approach that I saw on youtube wich uses storyboard.
On the cell that will use the internal UITableView I declared a function to set the delegate and dataSource of the internal tableView and gave to it a tag.
extension SurveyTableViewCell {
func setTableViewDataSourceDelegate<D:UITableViewDelegate & UITableViewDataSource>(_ dataSourceDelegate: D, forRow row: Int) {
subTableView.dataSource = dataSourceDelegate
subTableView.delegate = dataSourceDelegate
subTableView.reloadData()
}
}
Than on the viewController that manage the outer UITableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.idAnswer.text = "A."
cell.answer.text = "QUALQUER COISA"
return cell
}
if retrivedCell is SurveyTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: SurveyTableViewCell.identifier, for: indexPath) as! SurveyTableViewCell
cell.delegate = self
cell.setTableViewDataSourceDelegate(self, forRow: indexPath.row)
cell.setPositionRow(row: indexPath.row - 1)
cell.subTableView.rowHeight = UITableViewAutomaticDimension
cell.subTableView.estimatedRowHeight = 50
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1 {
return 3
}
var numberOfCells: Int = 0
if cellsToPresent != nil {
numberOfCells = cellsToPresent!.count
}
return numberOfCells + 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.selectRow()
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
The selectRow and deselectRow are methods to change the label of the cell of the inner tableView.
But still without success.
if I use the method:
tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
The app break complaining that I'm trying to dequeue different cells with the same indexPath.
Thanks for your help.
Don't use
let cell = tableView.dequeueReusableCell(withIdentifier:) in didSelect or didDeSelect methods.
Use
let cell = tableView.cellForRow(at: indexPath)
I hope this will help you.
I have an imageView inside my tableViewCell and i would like to have its image changed on selection. This is the code I have for it:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let myCell = tableView.cellForRow(at: indexPath) as! TableCell
myCell.resourceIcons.image = UIImage(named: "RubiusResources2")
tableView.deselectRow(at: indexPath, animated: true)
}
The code works, but some of the other rows in a different section further down the tableView also seem change.
EDIT:
Using the comments bellow I came to the following solution:
I first created a 2D bool array to the amount of sections and rows my table had and set them all to false.
var resourceBool = Array(repeating: Array(repeating:false, count:4), count:12)
I then created an if statement to check if the array at indexPath was false or true. This would be where the states of the image would change.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! TableCell
if (global.resourceBool[indexPath.section][indexPath.row] == false) {
myCell.resourceIcons.image = global.systemResourceImages[0]
} else if (global.resourceBool[indexPath.section][indexPath.row] == true) {
myCell.resourceIcons.image = global.systemResourceImages[1]
}
return myCell
}
Then, in the didSelectRow function I change the array at indexPath to true and reload the tableView data.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
global.resourceBool[indexPath.section][indexPath.row] = true
tableView.reloadData()
tableView.deselectRow(at: indexPath, animated: true)
}
From my understanding, the states of an object must always be in the cellForRow.
One of the solution would be that you need to maintain a seperate list of rows you selected, compare them in cellForRowAt method.
Code would look something like this.
var selectedArray : [IndexPath] = [IndexPath]()
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let myCell = tableView.cellForRow(at: indexPath) as! TableCell
myCell.resourceIcons.image = UIImage(named: "RubiusResources2")
tableView.deselectRow(at: indexPath, animated: true)
if(!selectedArray.contains(indexPath))
{
selectedArray.append(indexPath)
}
else
{
// remove from array here if required
}
}
and then in cellForRowAt, write this code to set proper images
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
.
.
.
if(selectedArray.contains(indexPath))
{
// use selected image
}
else
{
// use normal image
}
.
.
.
}
I have a TableViewController with static cells
I tried both creating segue from TableViewController and TableView cell (Not at the same time)
However, in both scenario, didSelectRowAtIndexPath was not fired
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("select")
}
I also have embedded collectionviewcontroller
class EventDetail: UITableViewController, UITextFieldDelegate, UICollectionViewDataSource, UICollectionViewDelegate {
What may be causing this?
Check your tableview selection must be single selection, if it is "No selection" then didSelectedRowAtIndex would not get called.
You can download sample code and observe it.
Sample download
Also check
Ideally your cellForRowAtIndex should be like this
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell : CustomCell! = tableView.dequeueReusableCellWithIdentifier("ID_CustomCell", forIndexPath: indexPath) as! CustomCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.lblData.text = "CustomCell....."
return cell
}
Swift 4 code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : CustomCell! = tableView.dequeueReusableCell(withIdentifier: "ID_CustomCell", for: indexPath) as! CustomCell
cell.selectionStyle = .none
cell.lblData.text = "CustomCell....."
return cell
}
call performseguewithidentifier from didselectRow if you have created segue from tableviewcontroller. like,
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("select")
self.performSegueWithIdentifier("your segue identifier", sender: self)
}
hope this will help :)