I have a ViewController that has a TableView. The tableView cell has a StackView with two images in it and outside it a label.
In the tableView:cellForRowAtIndexPath I get the cell and my data is inside a arrayList. And this is what I do:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "NamesTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NamesTableViewCell
let _name = _Names[indexPath.row]
if indexPath.row % 2 != 0 {
cell.backgroundColor = UIColor(hexString: "ededed")
}
cell.labelName.text = _name.name
if _name.gender! == "M" {
cell.pinkCircleImageView.hidden = true
} else if _name.gender! == "F" {
cell.blueCircleImageView.hidden = true
}
return cell
}
So as you can see I hide the images depending on the gender of the name and also change the background of every other cell.
Now, the behavior I am seeing is:
https://gyazo.com/1b2d39696892b7fb2f15b71696d9a925
The gender is available for every object, I've checked.
What do you guys think? Thanks!
cell.pinkCircleImageView.hidden = false
cell.blueCircleImageView.hidden = false
if _name.gender! == "M" {
cell.pinkCircleImageView.hidden = true
cell.bringSubviewToFront(blueCircleImageView)
} else if _name.gender! == "F" {
cell.blueCircleImageView.hidden = true
cell.bringSubviewToFront(pinkCircleImageView)
}
if indexPath.row % 2 == 0
{
cell.backgroundColor=UIColor.whiteColor()
}
else
{
cell.backgroundColor=UIColor(red: 248/255, green: 248/255, blue: 248/255, alpha: 1.0)
}
Related
So the issue is when a cell is tapped, desired data is shown and when again tapped on same cell ( again desired data is shown.)
But when one cell is selected and we again select other cell (then the data is been shown of second tapped cell but the first one is not deselected).
How can I take care of this issue?
var selectedIndex = -1
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIView.animate(withDuration: 1) {
self.labelViewHeightConstraint.constant = 60
self.labelLeadingConstraint.constant = 136
self.view.layoutIfNeeded()
}
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
if(selectedIndex == indexPath.row) {
selectedIndex = -1
print("deselect")
UIView.animate(withDuration: 0.4) {
cell.secondView.isHidden = true
cell.firstView.backgroundColor = UIColor(red: 0.8588, green: 0.84705, blue: 0.8745, alpha: 1.0)
}
} else {
cell.secondView.isHidden = false
}
self.expandTableView.beginUpdates()
//self.expandTableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic )
self.expandTableView.endUpdates()
}
You can archive single selection by setting tableView property like belwo
tableView.allowsMultipleSelection = false
This can also be done from Attributes Inspector
Hope this helps
you must disable multiple selection by,
self.tbl.allowsMultipleSelection = false
and enable single selection by,
self.tbl.allowsSelection = true
EDIT:-
if you want to access your old (selected cells), you should make a call like this,
//first assign tag or indexPath in Cell,
cell.tag = indexPath.row
// or
cell.indexPath = indexPath
//then fetch like bellow,
let visibleCell = tableView.visibleCells.filter({$0.tag == self.selectedIndex})
//or
let visibleCell = tableView.visibleCells.filter({$0.indexPath.row == self.selectedIndex})
//if you use ,
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
//then it will get you new cell object.
I'm using a tableview to display some categories in a left menu. When you select it, the data in the cells changes to courses ('Cursussen') within that category.
Each cell contains an UIImageView and an UILabel.
I noticed a while ago that when you select a course in the left menu, the label will change to that of a category. That wasn't a big issue back then, but now that I'm working to disable certain courses if they are not available it suddenly became a big issue. To indicate a course that's not available, I'm setting label.enabled = false, which works fine, however I also need to prevent the user from tapping on it and navigating to a course that's not available. To do that, I'm using tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) where I check whether the UILabel is enabled. If it's disabled the App won't navigate to the course.
Back to the issue, tapping on a course that is unavailable (which is displaying the correct image and label) will trigger the didSelectRowAtIndexPath delegate, but when I dequeue the Cell and check whether the UILabel in it is disabled it so happens to be enabled instead and furthermore the label.text does not equal the value I see in the App.
State before selecting a row:
State after selecting a row:
cellForRowAtIndexPath method:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("LeftCell", forIndexPath: indexPath)
let label = cell.contentView.subviews[0] as! UILabel
let image = cell.contentView.subviews[1] as! UIImageView
if(selectedCategory.ID > 0 || searchCursusses.count > 0) {
//Category is selected, load course into cell
var cursus : Cursus
if(selectedCategory.ID > 0) {
cursus = cursusses[indexPath.row] as Cursus
} else {
cursus = searchCursusses[indexPath.row] as Cursus
}
image.image = self.chooseImage(false, name: cursus.category!.Name)
label.numberOfLines = 0
label.text = cursus.name
label.lineBreakMode = .ByTruncatingTail
if(defaults.boolForKey("offline-mode")) {
let realm = try! Realm()
let videos = realm.objects(DownloadedVideo).filter("video.cursus.ID = %#", cursus.ID)
if(videos.count > 0) {
label.enabled = true
} else {
label.enabled = false
}
} else {
label.textColor = UIColor.blackColor()
}
cell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
cell.setSelected(false, animated: false)
} else {
let category = categories[indexPath.row] as Category
image.image = self.chooseImage(false, name: category.Name)
label.numberOfLines = 0
label.text = category.Name
label.lineBreakMode = .ByTruncatingTail
label.textColor = UIColor.blackColor()
cell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
}
return cell
}
didSelectRowAtIndexPath method:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier( "LeftCell", forIndexPath: indexPath)
cell.selectionStyle = .None
if(selectedCategory.ID > 0 || searchCursusses.count > 0) {
let label = cell.contentView.subviews[0] as! UILabel
if(!label.enabled) {
return
}
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let resultViewController = storyBoard.instantiateViewControllerWithIdentifier("CursusView") as! CursusViewController
if(selectedCategory.ID > 0) {
resultViewController.loadCursus(self.cursusses[indexPath.row], completionHandler: {
self.presentViewController(resultViewController, animated:true, completion:nil)
})
} else {
resultViewController.loadCursus(self.searchCursusses[indexPath.row], completionHandler: {
self.presentViewController(resultViewController, animated:true, completion:nil)
})
}
} else {
let category = categories[indexPath.row] as Category
cell.setSelected(true, animated: false)
let label = cell.contentView.subviews[0] as! UILabel
let image = cell.contentView.subviews[1] as! UIImageView
if(label.textColor == UIColor.lightGrayColor()) {
return
} else {
image.image = self.chooseImage(true, name: category.Name)
label.numberOfLines = 0
label.text = category.Name
label.textColor = UIColor.whiteColor()
label.lineBreakMode = .ByTruncatingTail
cell.backgroundColor = UIColor(red: 45.0/255.0, green: 145.0/255.0, blue: 220.0/255.0, alpha: 1.0)
self.selectedCategory = category
self.topBarTitle.text = category.Name
let realm = try! Realm()
let cursusses = realm.objects(Cursus).filter("category.ID = %#", selectedCategory.ID)
for cursus in cursusses {
self.cursusses.append(cursus)
}
let title = self.leftMenuNav.subviews[0] as! UILabel
let titleImg = self.leftMenuNav.subviews[1] as! UIImageView
titleImg.image = UIImage(named: "back-icon")
title.text = "Cursussen"
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.slideInFromRight(0.5)
self.tableView.reloadData()
self.collectionView.crossFade(0.3)
self.collectionView.reloadData()
})
}
}
}
It seems the old cells are not properly cleaned up after calling reloaddata, causing multiple cells to be at the same IndexPath.
I'm at a loss here, please help!
The problem is the line
let cell = tableView.dequeueReusableCellWithIdentifier( "LeftCell", forIndexPath: indexPath)
in the didSelectRowAtIndexPath function because it is creating a new cell (or trying to reuse one) and corrupting the table view cell cache.
You should be getting the existing cell instead using the cellForRowAtIndexPath function.
You have a problem with your approach: didSelectRowAtIndexPath is supposed to look at the data in the model, not in the view. Your code is trying, incorrectly, to access the cell and examine its labels etc. Instead, your method should be accessing the same underlying data source that has been used to make the labels in the first place.
In other words, instead of writing
let label = cell.contentView.subviews[0] as! UILabel
and then examining the enabled/disabled status of the label
you should write
cursus = cursusses[indexPath.row] as Cursus
and examine the availability of the cursus.
One general rule of thumb is that you should get very suspicious when you see code accessing components of UITableViewCell outside cellForRowAtIndexPath. Testing the state of a label is nearly universally an indication that the code is incorrect.
Man you are doing that wrong. Only tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell can dequeue cells and setup its contents.
To prevent selection you should react on func tableView(_ tableView: UITableView,
willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath and return nil to indicate that you don't want to select anything.
Main problem is that you are writing to complex methods. It is hard to figure out what are you doing and what is you intention. (will/did)SelectRowAtIndexPath should invoke only one/tow some simple methods, for example: perform a segue or load some data.
All my cells have a switch that moves the cell to the bottom of the table. It works fine, however when there is enough cells to drop below the visible bounds, I receive the error that the cell doesn't exist. This doesn't work because I need to update ALL cells sender.tag.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TaskTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as! TaskTableViewCell
var task = tasks[indexPath.row]
let index = tasks.indexOf(task)!
cell.cellSwitch.tag = index
cell.addSubview(cell.cellSwitch)
cell.tag = index
cell.cellSwitch.addTarget(self, action: "switched:", forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
func switched(sender: UIButton) {
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
let finalIndexPath = NSIndexPath(forRow: tasks.count - 1, inSection: 0)
let task = tasks[indexPath.row]
tasks.removeAtIndex(indexPath.row)
tasks.append(task)
self.taskTableView.moveRowAtIndexPath(indexPath, toIndexPath: finalIndexPath)
for var i = 0; i < tasks.count; i ++ {
let cellIndex = NSIndexPath(forRow: i, inSection: 0)
let cell: TaskTableViewCell = self.taskTableView.cellForRowAtIndexPath(cellIndex) as! TaskTableViewCell
cell.cellSwitch.tag = cellIndex.row
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellToBeReturned = UITableViewCell()
if indexPath.row != tableView.numberOfRowsInSection(0) - 1 {
let cell: TaskTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as! TaskTableViewCell
var task = tasks[indexPath.row]
cell.cellFlatSwitch.removeFromSuperview()
cell.currentStreak.removeFromSuperview()
switch(task.icon) {
case "heart"?:
task.color = colors[0]
case "meditate"?:
task.color = colors[1]
case "leaf"?:
task.color = colors[2]
case "run"?:
task.color = colors[3]
case "muscle"?:
task.color = colors[4]
default:
task.color = .blackColor()
}
switch(task.icon) {
case "heart"?:
cell.pretickButton.setImage(Icons.imageOfHeartIcon, forState: .Normal)
case "meditate"?:
cell.pretickButton.setImage(Icons.imageOfMeditationIcon, forState: .Normal)
case "run"?:
cell.pretickButton.setImage(Icons.imageOfRunIcon, forState: .Normal)
case "leaf"?:
cell.pretickButton.setImage(Icons.imageOfLeafIcon, forState: .Normal)
case "muscle"?:
cell.pretickButton.setImage(Icons.imageOfMuscleIcon, forState: .Normal)
default:
cell.pretickButton.setImage(Icons.imageOfLeafIcon, forState: .Normal)
}
if task.longestStreak >= 21 {
cell.starImage.alpha = 1
} else {
cell.starImage.alpha = 0
}
if localSettings.habitPreview == "dayCounter" {
cell.currentStreak = LTMorphingLabel(frame: CGRectMake(11, 29, 39, 31))
cell.currentStreak.morphingEffect = LTMorphingEffect.Anvil
cell.currentStreak.textAlignment = NSTextAlignment.Center
cell.currentStreak.text = String(task.consecutiveDays!)
cell.addSubview(cell.currentStreak)
cell.streakSublabel.textAlignment = .Center
cell.currentStreak.adjustsFontSizeToFitWidth = true
cell.streakSublabel.adjustsFontSizeToFitWidth = true
cell.growthPreviewImage.hidden = true
cell.streakSublabel.hidden = false
} else {
if task.growth > 0 && task.growth <= 21 {
cell.growthPreviewImage.image = UIImage(named: "growth\(task.growth!)")
} else if task.growth <= 0 {
cell.growthPreviewImage.image = UIImage(named: "growth1")
} else if task.growth > 21 {
cell.growthPreviewImage.image = UIImage(named: "growht21")
}
cell.growthPreviewImage.hidden = false
cell.streakSublabel.hidden = true
}
if task.consecutiveDays == 1 {
cell.streakSublabel.text = "day"
} else {
cell.streakSublabel.text = "days"
}
cell.taskLabel.adjustsFontSizeToFitWidth = true
cell.taskLabel.text = task.name
cell.taskLabel.textColor = task.color
cell.cellFlatSwitch = AIFlatSwitch(frame: CGRectMake(300, 15, 70, 70))
cell.cellFlatSwitch.lineWidth = 2.3
cell.cellFlatSwitch.strokeColor = task.color!
cell.cellFlatSwitch.trailStrokeColor = task.color!
let index = tasks.indexOf(task)!
cell.pretickButton.tag = index
cell.backgroundColor = UIColor.clearColor()
cell.addSubview(cell.cellFlatSwitch)
cell.tag = index
cell.bringSubviewToFront(cell.pretickButton)
cell.pretickButton.addTarget(self, action: "switched:", forControlEvents: UIControlEvents.TouchUpInside)
cell.sendSubviewToBack(cell.cellFlatSwitch)
if task.doneToday == true {
cell.cellFlatSwitch.selected = true
cell.pretickButton.alpha = 0.0101
} else {
cell.cellFlatSwitch.setSelected(false, animated: false)
cell.pretickButton.alpha = 1.0
}
cellToBeReturned = cell
} else {
let cell: AddNewTableViewCell = tableView.dequeueReusableCellWithIdentifier("add") as! AddNewTableViewCell
cell.tag = indexPath.row
cellToBeReturned = cell
}
return cellToBeReturned
}
Any tips or help is greatly appreciated.
You are doing it wrong.
Don't call methods with NSIndexPath object references to move cells around. The implementation of UITableView suggests you should use one data source (could be an array, a dictionary, a custom NSObject, NSFetchedResultsController, etc) to implement all the relevant UITableViewDataSource and UITableViewDelegate delegate methods and you make updates to your data source first then call either reloadData or beginUpdates and endUpdates on your UITableView whenever you need to refresh your view.
In your case, I would simply use 2 NSMutableArray's, one for items with, one for without tags. Your switch handler should retrieve the item from the cell index path and move it from one array to the other. Then you can call beginUpdates and move your cell appropriately.
They key is to make changes to your data source first and in a way that is consistent to your UITableViewDataSource methods, especially the cellForAtIndexPath method. Basically the error you get is most likely because your data source is out of sync with the visible cells.
Hope this helps.
Currently working with UITableView in swift.
Since my table cell values are appending by clicking the button i want to figure out a way to disable interaction for all the previous cells, except the last one.
I've played with array-based approach a little, but it didn't work out the way i wanted.
var disabledRows = [0,1,2]
else
{
cell.backgroundColor = UIColor.orangeColor()
}
if (contains(disabledRows, indexPath.row)) {
cell.userInteractionEnabled = false
}
else {
cell.userInteractionEnabled = true
}
The issue with this approach is that i have to count all cells and then block the last one.
Maybe there are some easier approaches?
Any help much appreciated.
Update - here is my complete "cellForRowAtIndexPath" function
func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell
{
let cell: CustomCellForTableViewTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CustomCellForTableViewTableViewCell
if indexPath.row % 2 == 0
{
cell.backgroundColor = UIColor.purpleColor()
}
else
{
cell.backgroundColor = UIColor.orangeColor()
}
if (contains(disabledRows, indexPath.row)) {
cell.userInteractionEnabled = false
}
else {
cell.userInteractionEnabled = true
}
let quest = arrayOfQuestions[indexPath.row]
cell.setCell(type.Grocery, optionno1:type.option1, optionno2:type.option2)
cell.mainText.text = type.Grocery
cell.optionOne.backgroundColor = UIColor.redColor()
return cell
}
In cellForRowAtIndexPath: method.
if (indexPath.row == yourDataSourceArray.count - 1) {
// enable cell
} else {
// disable cell
}
I've put an ImageView in a UITableViewCell on my Storyboard but at run time it is not showing up on my simulator. How do I solve this?
My storyboard:
The object overview of my storyboard:
The Swift code that fills up the cells:
func tableView(gamesListTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = gamesListTableView.dequeueReusableCellWithIdentifier("Game", forIndexPath: indexPath) as! UITableViewCell
if (indexPath.row < GamesList.count) {
cell.detailTextLabel?.text = GamesList[indexPath.row]["game_guid"].string!;
cell.textLabel?.text = GamesList[indexPath.row]["opponent_name"].string!;
if (GamesList[indexPath.row]["self_turn"] == false) {
cell.textLabel?.textColor = UIColor.greenColor();
} else if (GamesList[indexPath.row]["game_idle_time"] <= 60) {
cell.textLabel?.textColor = UIColor.yellowColor();
} else {
cell.textLabel?.textColor = UIColor.redColor();
}
} else {
cell.detailTextLabel?.text = InvitesList[indexPath.row - GamesList.count]["invite_guid"].string!;
cell.textLabel?.text = InvitesList[indexPath.row - GamesList.count]["invite_text"].string!;
cell.textLabel?.textColor = UIColor.blueColor();
}
return cell
}
What it looks like at runtime:
You should probably pause the execution and view your view hierarchy in Xcode. Check what the frame size it at run time and see if A.) something is overlapping it or B.)If your constraints are wrong.