My development environment is swift3, xcode8.
I'm making a list app like Apple's message app.
When I select the list in the table view, I go to the detail page (through the seg) and now I want to implement multiple delete functions, but there's a problem. When I edit mode, I can see the selection window, but if I select that selection window, just go to the detail page.
Maybe before going to the detail page through Seg. I think I should make it a multiple choice. What should I do?
Make sure you conform something like below code;
class TableviewController:UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
var isMultipleSelectionActive = false
var selectedItems: [String: Bool] = [:]
tableView.allowsMultipleSelectionDuringEditing = true
tableView.setEditing(true, animated: false)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = "\(indexPath.row)"
return cell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = items.objectAtIndex(indexPath.row)
//add to selectedItems
selectedItems[selectedItem] = true
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = items.objectAtIndex(indexPath.row)
// remove from selectedItems
selectedItems[selectedItem] = nil
}
func getStatusOfSelectedItems() {
for item in selectedItems {
println(item)
}
}
//You should override shouldPerformSegueWithIdentifier and return false if isMultipleSelectionActive is true
override func shouldPerformSegue(withIdentifier identifier: String?, sender: Any?) -> Bool {
if let identifierName = identifier {
if identifierName == "NameOfYourSegueIdentifier" {
if isMultipleSelectionActive {
return false
}
}
}
return true
}
}
This code used to select the multiple row
class TableViewController: UITableViewController
{
var lastSelectedIndexPath = NSIndexPath(forRow: -1, inSection: 0)
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath)
// Configure the cell...
cell.textLabel!.text = "row: \(indexPath.row)"
if cell.selected
{
cell.selected = false
if cell.accessoryType == UITableViewCellAccessoryType.None
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
}
else
{
cell.accessoryType = UITableViewCellAccessoryType.None
}
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let cell = tableView.cellForRowAtIndexPath(indexPath)
if cell!.selected
{
cell!.selected = false
if cell!.accessoryType == UITableViewCellAccessoryType.None
{
cell!.accessoryType = UITableViewCellAccessoryType.Checkmark
}
else
{
cell!.accessoryType = UITableViewCellAccessoryType.None
}
}
}
Related
I have a UITableViewcontroller setup with just two cells. The footer text is displaying over the last cell.
Strangely I have other controllers with practically the same setup and code and where the footer is showing as expected.
I have tried tried changing the style group / inset etc.
Any ideas appreciated. Thanks
import UIKit
class LanguagesTableViewController: UITableViewController {
var checked = [Bool]()
var choices = ["English","French"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = false
let defaults = UserDefaults.standard
checked = defaults.array(forKey: "Language") as? [Bool] ?? [true, false]
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateSwitchState()
tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "languageChoiceCell", for: indexPath)
cell.textLabel?.text = choices[indexPath.row]
if !checked[indexPath.row] {
cell.accessoryType = .none
} else if checked[indexPath.row] {
cell.accessoryType = .checkmark
}
return cell
}
override func numberOfSections(in 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 choices.count
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if checked[indexPath.row] {
tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableView.ScrollPosition.none)
}
}
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return Constants.languagesFooterText
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// If we are selecting a row that is already checked we do nothing
guard !checked[indexPath.row] else { return }
// Reset all checked state.
checked = [Bool](repeating: false, count: choices.count)
// And set the current row to true.
checked[indexPath.row] = true
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
checked[indexPath.row] = false
} else {
cell.accessoryType = .checkmark
checked[indexPath.row] = true
}
}
updateSwitchState()
}
// did ** DE ** Select
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
checked[indexPath.row] = false
} else {
cell.accessoryType = .checkmark
checked[indexPath.row] = true
}
}
updateSwitchState()
}
func updateSwitchState() {
let defaults = UserDefaults.standard
defaults.set(checked, forKey: "Language")
}
}
You should set a height on your footer view. You can do this by calling tableView(:heightForFooterInSection:) and returning UITableView.automaticDimension.
As you are inheriting from UITableViewController you can do it in the following way
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
UITableView.automaticDimension
}
This will give you the following result:
How to show button Highlight colour,,and the colour is red
extension LeaveDetailVC: cellIndexCall{
func selectBtnIndex(sender: UIButton) {
let buttonPosition:CGPoint = sender.convert(.zero, to:leaveDetailTableView)
var indexPath = leaveDetailTableView.indexPathForRow(at: buttonPosition)
self.indexPath = indexPath!
print("\(String(describing: indexPath?.row))") /* index path of button
self.menuClickIndex = (indexPath?.row)!
}
}
And my Button is cover the tableViewCell.
And I also create a button delegate in cell class and call in viewController through extension.
I just want to highlight button and that is over cell.
override func tableView(_ tableView: UITableView, shouldHighlightRowAt
indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, didHighlightRowAt
indexPath:
IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell?.contentView.backgroundColor = UIColor.orange
cell?.backgroundColor = UIColor.orange
}
override func tableView(_ tableView: UITableView, didUnhighlightRowAt
indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell?.contentView.backgroundColor = UIColor.black
cell?.backgroundColor = UIColor.black
}
In your viewController
class LeaveDetailVC: ViewController {
var selectedRows: [IndexPath] = [] // i assume multiple select button
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YOUR_CELL") as! YOUR_CELL_CLASS
cell.isHighlight = selectedRows.indices.contains(indexPath) // Determine if cell was selected or not
}
}
extension LeaveDetailVC: cellIndexCall {
func selectBtnIndex(sender: UIButton) {
let buttonPosition:CGPoint = sender.convert(.zero, to:leaveDetailTableView)
var indexPath = leaveDetailTableView.indexPathForRow(at: buttonPosition)
if selectedRows.indices.contains(indexPath) {
if let index = selectedRows.index(of: indexPath) {
self.selectedRows.remove(at: index) // remove selected indexpath
}
} else {
self.selectedRows.append(indexPath) // add selected indexpath
}
}
}
In your cell class
var isHighlight: Bool = false
override func layoutSubviews() {
super.layoutSubviews()
// TODO : Set your button color based on isHighlight flag
}
Im using a tableview to display an array of strings. When I click on a particular row, I want it to be highlighted with a checkmark. When I deselect a row, I want the checkmark to be removed. When I press a button, I want the rows that are currently highlighted to be passed out in an array(newFruitList).
My problem is that when I click the first row, the last is highlighted. When I uncheck the first row, the last is unchecked, as if they are the same cell?
How do I overcome this?
Also, the way I am adding and removing from my new array, is this the correct way to go about doing this?
Thanks
My Code:
class BookingViewController: UIViewController, ARSKViewDelegate, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var table: UITableView!
let fruits = ["Apples", "Oranges", "Grapes", "Watermelon", "Peaches"]
var newFruitList:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.table.dataSource = self
self.table.delegate = self
self.table.allowsMultipleSelection = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = table.dequeueReusableCell(withIdentifier: "Cell")
if cell == nil{
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
}
cell?.textLabel?.text = fruits[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
newFruitList.append(fruits[indexPath.row])
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = newFruitList.index(of: fruits[indexPath.row]) {
newFruitList.remove(at: index)
}
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
}
}
#IBAction func bookButtonPressed(_ sender: UIButton) {
//testing purposes
for i in stride(from: 0, to: newFruitList.count, by: 1){
print(newFruitList[i])
}
}
They are probably the same cell because you use dequeueReusableCell and it reuses old cells.
use:
override func prepareForReuse() {
super.prepareForReuse()
}
To reset the cell and it should be fine.
As for the save and send mission. Create an pre-indexed array that you can populate.
var selected: [Bool] = []
var fruits: [Fruit] = [] {
didSet {
selected = Array(repeating: false, count: fruits.count)
}
}
And in your didSelectItemAt you do:
selected[indexPath.item] = !selected[indexPath.item]
UITableView reuses the cell that is already present and hence you will see that duplicate check mark, so to solve this issue you need to clear the cell states while loading cell. for that you can create a model with property to track the states of your selected cells
So your fruit model must be like below
class Fruit{
var name:String
var isSelected:Bool
init(name:String){
isSelected = false
self.name = name
}
}
Then you will have table view populated with Fruit list
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = table.dequeueReusableCell(withIdentifier: "Cell")
if cell == nil{
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
}
let model = fruits[indexPath.row]
cell?.textLabel?.text = model.name
if(model.isSelected){
cell.accessoryType = .checkmark
}
else{
cell.accessoryType = .none
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
newFruitList.append(fruits[indexPath.row])
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
var model = fruits[indexPath.row]
model.isSelected = true
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = newFruitList.index(of: fruits[indexPath.row]) {
newFruitList.remove(at: index)
}
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
var model = fruits[indexPath.row]
model.isSelected = false
}
}
I m using swift 2 and UITableViews and when I press a cell a checkmark appear, but I wan't that only one cell can be checked in my tableview so the other checkmarks will disappear from my tableview. I tried different technics without success. I have a CustomCell with just a label.
Here is my code :
import UIKit
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var tableView: UITableView!
var answersList: [String] = ["One","Two","Three","Four","Five"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return answersList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCustomCell", forIndexPath: indexPath) as! MyCustomCell
cell.displayAnswers(answersList[indexPath.row]) // My cell is just a label
return cell
}
// Mark: Table View Delegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Element selected in one of the array list
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark {
cell.accessoryType = .None
} else {
cell.accessoryType = .Checkmark
}
}
}
}
Assuming you've only section here's what you can do
// checkmarks when tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let section = indexPath.section
let numberOfRows = tableView.numberOfRowsInSection(section)
for row in 0..<numberOfRows {
if let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section)) {
cell.accessoryType = row == indexPath.row ? .Checkmark : .None
}
}
}
Fixed code from #SirH to work with Swift 3
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let section = indexPath.section
let numberOfRows = tableView.numberOfRows(inSection: section)
for row in 0..<numberOfRows {
if let cell = tableView.cellForRow(at:IndexPath(row: row, section: section)) {
cell.accessoryType = row == indexPath.row ? .checkmark : .none
}
}
}
I'm loading a tableView from a plist file. This works with no problems. I just simply want to "tick" the selected rows. At the moment, with my code it didn't work as desired. At the moment, it looks as below:
tap row1 (it will tick row 1 = good)
tap row1 again (nothing happens = bad. I expect here the row to be unticked)
While tapping again on row 1, it unticks then. After the second tap on it.
when I tap row0 at the initial load of the tableview it never ticks me the row
my code:
class portals: UITableViewController {
var lastSelectedIndexPath = NSIndexPath(forRow: -1, inSection: 0)
...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel!.text = portals[indexPath.row]
return cell
}
// Check which portal is selected
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var whichPortalIsSelected: String = ""
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
// Tick the selected row
if indexPath!.row != lastSelectedIndexPath?.row {
let newCell = tableView.cellForRowAtIndexPath(indexPath!)
newCell?.accessoryType = .Checkmark
lastSelectedIndexPath = indexPath
whichPortalIsSelected = newCell!.textLabel!.text!
println("You selected cell #\(lastSelectedIndexPath.row)!") //PPP
println("You selected portal #\(whichPortalIsSelected)!") //PPP
// Un-Tick unselected row
} else {
let newCell = tableView.cellForRowAtIndexPath(indexPath!)
newCell?.accessoryType = .None
whichPortalIsSelected = newCell!.textLabel!.text!
println("You unselected cell #\(indexPath!.row)!") //PPP
println("You unselected portal #\(whichPortalIsSelected)!") //PPP
}
}
}
Swift 4
First, make your tableView support multiple selection :
self.tableView.allowsMultipleSelection = true
self.tableView.allowsMultipleSelectionDuringEditing = true
Then simply subclass UITableViewCell like this :
class CheckableTableViewCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
self.accessoryType = selected ? .checkmark : .none
}
}
Finally, use it in your cellForRowAt indexPath as such :
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as? CheckableTableViewCell
If you have to, don't forget to subclass your prototype cell in your xib/storyboard :
First of all, go to your Storyboard and select you tableview and in the Attributes Inspector, set Selection to Multiple Selection.
Attributes Inspector with multiple selection
Then, override the setSelected(_ selected: Bool, animated: Bool) function in the subclass of UITableViewCell.
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
accessoryType = selected ? UITableViewCellAccessoryType.checkmark : UITableViewCellAccessoryType.none
}
This enable untick.
class TableViewController: UITableViewController
{
var lastSelectedIndexPath = NSIndexPath(forRow: -1, inSection: 0)
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath)
// Configure the cell...
cell.textLabel!.text = "row: \(indexPath.row)"
if cell.selected
{
cell.selected = false
if cell.accessoryType == UITableViewCellAccessoryType.None
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
}
else
{
cell.accessoryType = UITableViewCellAccessoryType.None
}
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let cell = tableView.cellForRowAtIndexPath(indexPath)
if cell!.selected
{
cell!.selected = false
if cell!.accessoryType == UITableViewCellAccessoryType.None
{
cell!.accessoryType = UITableViewCellAccessoryType.Checkmark
}
else
{
cell!.accessoryType = UITableViewCellAccessoryType.None
}
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 100
}
}
you have to make a costume class to get the selected state of the cell where you must override a func called
setSelected(_ selected: Bool, animated: Bool)
or the tick will be displayed randomly as you scroll ...
here is an example of what i did:
1- created a class for the cell
2- added an outlet for an image to display the tick (you can escape this if you don't want a costume tick image)
3- overrided the function and used the param selected :D
here is my class:
import UIKit
class AddLocationCell: UITableViewCell {
#IBOutlet weak var check: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected{
check.image = UIImage(named:"check_active")
}else{
check.image = UIImage(named:"check_normal")
}
// Configure the view for the selected state
}
}
There are many solutions to this problem, here's one I came up with. I am using the built in cell "selected" property so the tableview saves it for us. Just make sure in your storyboard or when you setup the tableview in code you use multiple selection.
import UIKit
class TableViewController: UITableViewController
{
var lastSelectedIndexPath = NSIndexPath(forRow: -1, inSection: 0)
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel!.text = "row: \(indexPath.row)"
if cell.selected
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
}
else
{
cell.accessoryType = UITableViewCellAccessoryType.None
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let cell = tableView.cellForRowAtIndexPath(indexPath)
if cell!.selected == true
{
cell!.accessoryType = UITableViewCellAccessoryType.Checkmark
}
else
{
cell!.accessoryType = UITableViewCellAccessoryType.None
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 100
}
}
I made a sample project here: https://github.com/brcimo/SwiftTableViewMultipleSelection