This question already has an answer here:
Table view didSelectRowAtIndexPath, only works after selecting second click
(1 answer)
Closed 7 years ago.
So here's the thing:
I implemented a UITableViewController with a custom cell..
Then i replaced the automatic segue cell to viewcontroller (storyboard) with viewcontroller to viewcontroller.
Code from my UITableViewController:
// MARK: - Tableview managment
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var lobbycell = tableView.dequeueReusableCellWithIdentifier("lobbycell") as! LobbyTableViewCell
let row = indexPath.row
if let channel = lobbies[row].lobbyName {
lobbycell.lobbynameLabel.text = channel
}
if let usercount = lobbies[row].users?.count {
lobbycell.usercountLabel.text = "Anzahl Spieler: \(usercount)"
}
if let host = lobbies[row].host?.username {
lobbycell.hostnameLabel.text = "Host: \(host)"
}
return lobbycell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lobbies.count
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let selectedLobby = lobbies[indexPath.row]
var loading = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
PFLobby.joinLobbyInBackgroundWithBlock(selectedLobby, user: PFUser.currentUser()!) { (success, error) -> Void in
loading.hide(true)
if success {
self.performSegueWithIdentifier("joinLobby", sender: self)
} else {
}
}
}
Now here's the problem:
If I click on a cell it's going to get gray (like selected) but won't call the didDeselectRowAtIndexPathfunc..
Only If I click on another cell it's going to call the didDeselectRowAtIndexPathwith the gray (like selected) cell's indexPath.
Are you confused with didSelectRowAtIndexPath and didDeselectRowAtIndexPath?
The latter will only be called if it's deselected, hence why it's called after you select another cell.
If you want to call the didDeselectRowAtIndexPath, then add the method deselectRowAtIndexPath in didSelectRowAtIndexPath and it will deselect right way.
This is expected behavior. tableView(_:didDeselectRowAtIndexPath:) will only be called after another cell is selected.
If you want, it is possible to intercept the selection event in tableView(:_willSelectRowAtIndexPath:):
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
if let selectedIndex = tableView.indexPathForSelectedRow() where selectedIndex == indexPath {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
return nil
}
return indexPath
}
The index path returned by this method will be selected. If you return nil, nothing will get selected.
Related
I am trying to create a contacts page where you can see all your contacts with a friend request cell showing up when you receive a friend request, but not there when you do not have any. At the moment, both custom cells work fine. The issue I have is that the contactRequestTableViewCell overlaps the first cell of the contactListTableViewCell.
I have researched other questions about two custom tableviewcells and none are quite having the same issues that I am facing.
Here is my executing code at the moment, I am returning 2 sections in the table view.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! ContactListTableViewCell
let requestCell = tableView.dequeueReusableCellWithIdentifier("requestCell", forIndexPath: indexPath) as! ContactRequestsTableViewCell
let user = OneRoster.userFromRosterAtIndexPath(indexPath: indexPath)
if (amountOfBuddyRequests > 0) {
if (indexPath.section == 0) {
requestCell.hidden = false
cell.hidden = false
requestCell.friendRequestLabel.text = "test"
} else if (indexPath.section >= 1) {
cell.contactNameLabel!.text = user.displayName;
cell.contactHandleLabel!.text = "# " + beautifyJID(user.jidStr)
cell.contactHandleLabel!.textColor = UIColor.grayColor()
OneChat.sharedInstance.configurePhotoForImageView(cell.imageView!, user: user)
}
return cell;
}
else { // if buddy requests == 0
requestCell.hidden = true
cell.contactNameLabel!.text = user.displayName;
cell.contactHandleLabel!.text = "# " + beautifyJID(user.jidStr)
cell.contactHandleLabel!.textColor = UIColor.grayColor()
print ("This is how many unreadMessages it has \(user.unreadMessages)")
// If there is unread messages for a person highlight it blue
// However this feature isn't working right now due to unreadMessages bug
if user.unreadMessages.intValue > 0 {
cell.backgroundColor = .blueColor()
} else {
cell.backgroundColor = .whiteColor()
}
OneChat.sharedInstance.configurePhotoForCell(cell, user: user)
return cell;
}
}
This is the current output that I have right now, my cells that have "test" are covering up other contactListTableViewCells.
The function tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell should always return one and the only one TableViewCell you want at indexPath, so you don't want to always return cell of type ContactListTableViewCell.
According to documentation, the cellForRowAtIndexPath tableView method asks for the cell at the indexPath, which means literally there can only be one cell at certain row of a certain section, so returning two cells is not an option.
I suggest you use two arrays to store the requests and contacts information. For example, you have arrays requests and contacts. Then you can tell the tableView how many rows you want:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return requests.count + contacts.count
}
and then in cellForRowAtIndexpath you do something like:
override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row < requests.count {
// return a request cell
}
else {
// return a contact cell
}
}
I'm only using one tableView section here. If you still want two sections you can simply return 2 in numberOfSections function and add if statements in cellForRowAtIndexPath for indexPath.section.
Hope this helps.
It turns out that the issue was dealing with the data sources. My data sources were not pointing to the correct tableviewcell. This resulted in them pointing to an incorrect cell. This issue was fixed by remaking the data sources system that was in place. This issue will not affect most as the data sources will point to the correct tableviewcell by default.
Contrary to what another poster said, you can in fact display two or more custom cells in a single table. This is how I fixed the tableView display issues:
var friendRequests = ["FriendRequest1", "FriendRequest2"]
var contacts = ["User1","User2","User3","User4"]
var amountOfBuddyRequests = 1
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if (amountOfBuddyRequests > 0) {
return 2
}
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (amountOfBuddyRequests > 0) {
if (section == 0) {
return friendRequests.count
}
}
return contacts.count
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (amountOfBuddyRequests > 0) {
if (indexPath.section == 0) {
let requestCell = tableView.dequeueReusableCellWithIdentifier("requestCell") as! ContactRequestsTableViewCell
requestCell.friendRequestLabel.text = friendRequests[indexPath.row]
requestCell.onButtonTapped = {
self.friendRequests.removeAtIndex(indexPath.row)
self.tableView.reloadData()
}
requestCell.addButtonTapped = {
self.addUser(self.friendRequests[indexPath.row])
self.friendRequests.removeAtIndex(indexPath.row)
self.tableView.reloadData()
}
return requestCell
}
}
let friendCell = tableView.dequeueReusableCellWithIdentifier("FriendCell") as! ContactListTableViewCell
friendCell.contactNameLabel.text = contacts[indexPath.row]
return friendCell
}
I want to present a different view when selecting each Cell in a Table View.
I figured out that I have to use this function:
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
What should I add inside the function to make this work?
First of all, you need didSelectRowAtIndexPath and inside the function you could use indexPath.row to know witch cell was tapped and then you need to create and push the view you want
let viewControllerObj = self.storyboard?.instantiateViewControllerWithIdentifier("ViewControllerIdentifier") as? ViewController
self.navigationController?.pushViewController(viewControllerObj!, animated: true)
also, make sure your navigationController is not nil
...hope it helps
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
if indexPath.row == 0
{
let objOne = self.storyboard?.instantiateViewControllerWithIdentifier("ID_OneScene") as? OneScene
self.navigationController?.pushViewController(objOne!, animated: true)
}
else if indexPath.row == 1
{
let objTwo = self.storyboard?.instantiateViewControllerWithIdentifier("ID_TwoScene") as? TwoScene
self.navigationController?.pushViewController(objTwo!, animated: true)
}
else
{
let objOther = self.storyboard?.instantiateViewControllerWithIdentifier("ID_OtherScene") as? OtherScene
self.navigationController?.pushViewController(objOther!, animated: true)
}
}
I have an app that has a tableview embedded in a ViewController and whenever I navigate to another ViewController and navigate back to the table view, the cells repeat when I scroll. Does anyone have any advice on how to prevent this? The current code for the tableview is :
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return marathonRaces.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let singleCell: marathonTableViewCell = tableView.dequeueReusableCellWithIdentifier("marathonCell") as! marathonTableViewCell
singleCell.marathonName.text = marathonRaces[indexPath.row]
singleCell.entry.text = "\(entryNumber[indexPath.row])"
singleCell.entries.text = "\(entires[indexPath.row])"
singleCell.length.text = "\(length[indexPath.row])"
return singleCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = self.marathonsTableView.indexPathForSelectedRow!
let currentCell = marathonsTableView.cellForRowAtIndexPath(indexPath) as! marathonTableViewCell
let marathonEvents = currentCell.marathonName.text
self.performSegueWithIdentifier("marathonDetail", sender: self)
}
I am using swift, xcode7, and parse as my backend
the only relevant code within viewDidAppear would be :
var query = PFQuery(className: “Marathons")
query.orderByAscending("end")
query.findObjectsInBackgroundWithBlock { (marathons, error:NSError?) -> Void in
if(error == nil ){
//success
for marathon in marathons! {
self.marathonRaces
.append(marathon[“marathonName"] as! String)
self.entry.append(marathon[“entryNumber"] as! Int)
self.entries.append(marathon[“entries"] as! Int)
self.length.append(marathon[“length"] as! Int)
}
self.marathonsTableView.reloadData()
}else {
print(error)
}
}
The problem is in your viewDidAppear method. Every time the controller appears you fetch data from background, append them to your arrays and reload the tableview. Move the code for fetching data to viewDidLoad for example and "repeating" should be gone.
Have you checked to see if the datasource marathonRaces is gaining more entries?
You may be adding more entries on each back navigation, if so either do not add them or remove all entries prior to adding them.
I'm having trouble adding rows to the UITableView upon UIButton click.
I have two custom-cell xibs - one that contains an UILabel, another one that contains an UIButton.
Data for the table cell is loaded from two dictionaries (answersmain and linesmain).
Here is the code for the UITableView main functions:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.linesmain["Audi"]!.count + 1
}
// 3
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(indexPath.row < 3){
var cell:TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! TblCell
cell.lblCarName.text = linesmain["Audi"]![indexPath.row]
return cell
} else {
var celle:vwAnswers = self.tableView.dequeueReusableCellWithIdentifier("cell2") as! vwAnswers
celle.Answer.setTitle(answersmain["Good car"]![0], forState:UIControlState.Normal)
return celle
}}
What do I put here?
#IBAction func option1(sender: UIButton) {
// I need to add rows to the uitableview from two dictionaries into two different xibs
}
You can do the next:
var showingAll = false
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return showingAll ? self.linesmain["Audi"]!.count + 1 : 0
}
#IBAction func option1(sender: UIButton) {
showingAll = true
tableView.beginUpdates()
let insertedIndexPathRange = 0..<self.linesmain["Audi"]!.count + 1
var insertedIndexPaths = insertedIndexPathRange.map { NSIndexPath(forRow: $0, inSection: 0) }
tableView.insertRowsAtIndexPaths(insertedIndexPaths, withRowAnimation: .Fade)
tableView.endUpdates()
}
You should take a look over the documentation here
There is this UITableView method called insertRowsAtIndexPaths:withRowAnimation: that inserts row at a specified indexPath.
You need to modify linesmain and answersmain by adding data to these and then call [self.tableView reloadData].
It would be better if you extract linesmain["Audi"] and answersmain["Good car"] and save them into different mutable arrays and modify those.
You need to do this in the func option1.
I have been struggling with this issue. I can scroll freely between the tag cells because it actually remembers them. But if I get the description cell out of my view it immediately removes it from memory and doesn't get it back. Instead I just get "fatal error: unexpectedly found nil while unwrapping an Optional value" when I scroll back to the description. So I have the following pieces of code:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44.0
tableView.reloadData()
}
I don't know if the viewWillAppear is of any importance in this case but if it is then tell me. Anyway, this is for filling in the cells in my table view:
func GetDescription(cell:descCell, indexPath: NSIndexPath) {
cell.descText.text = descriptTextTwo.htmlToString
}
func GetTagCell(cell:basicTag, indexPath: NSIndexPath) {
let item = tagResults[indexPath.row]!
cell.titleLabel.text = item["tagname"]?.htmlToString
}
func GetValueCell(cell: basicTag, indexPath: NSIndexPath) {
let item = tagResults[indexPath.row]!
cell.valueLabel.text = item["value"]?.htmlToString
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if filledDescription == false {
return getDescriptionAtIndexPath(indexPath)
} else {
return getTagAtIndexPath(indexPath)
}
}
func getDescriptionAtIndexPath(indexPath:NSIndexPath) -> descCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier(descriptionCell) as descCell
GetDescription(cell, indexPath: indexPath)
filledDescription = true
return cell
}
func getTagAtIndexPath(indexPath: NSIndexPath) -> basicTag {
let cell = self.tableView.dequeueReusableCellWithIdentifier(tagCell) as basicTag
GetTagCell(cell, indexPath: indexPath)
GetValueCell(cell, indexPath: indexPath)
return cell
}
So how can I make Swift remember what is in the first cell? Because I am guessing that that is what happens, that it removes what was in the first cell as soon as you get it out of the view. I am guessing I have to do something with "indexPath" but I am not exactly sure how to implement it in this case and if I am far off, please tell me what I am doing wrong. Thanks!
Change the following :
if filledDescription == false {
return getDescriptionAtIndexPath(indexPath)
} else {
return getTagAtIndexPath(indexPath)
}
With:
if indexPath.row == 0 {
return getDescriptionAtIndexPath(indexPath)
} else {
return getTagAtIndexPath(indexPath)
}
This will make sure that the first cell in the table will always treated as a "Description" cell. Since the filledDescription never becomes false after your set it to true, when you get back to the first cell it is treated as a "Tag" cell (due to the if line) where in fact the reusable cell contains "Description" cell data