Limit the selection on UITableView to 2 - ios

How can I do to limit the selection of row in tableview to 2.
I mean I have a tableview with items but the user can select unlimited rows. I want to set to 2 selected row maximum.
I just made a listeSiropsSelected and put items on it, and if this list .count >= 2, display an alert. But the 3rd row is selected even if alert is show.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Tableview setup \(listeSirops.count) items")
return listeSirops.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "siropCell", for: indexPath) as? ChoisirSiropTableViewCell else {
fatalError("Unable to dequeue tabacCell")
}
let tabac = listeSirops[indexPath.row]
cell.labelNom.text = tabac.nom
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sirops = listeSirops[indexPath.row]
if(listeSiropsSelected.count >= 2) {
let alert = UIAlertController(title: "Erreur", message: "Vous ne pouvez choisir que 2 sirops.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
}))
self.present(alert, animated: true, completion: nil)
} else {
listeSiropsSelected.append(sirops.nom)
print(listeSiropsSelected)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let sirops = listeSirops[indexPath.row]
let objectToRemove = sirops.nom
listeSiropsSelected.remove(object: objectToRemove)
print(listeSiropsSelected)
}
3 rows selected, message appear but how can remove the 3rd row ?

Implement willSelectRowAt, it prevents the selection of a cell if nil is returned
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return listeSiropsSelected.count < 3 ? indexPath : nil
}
Delete the alert, it's not needed, the user experience is that the user is not able to select more than 2 cells.

Just add this line under self.present:
tableView.deselectRow(at: indexPath, animated: true)

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
//here
if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.count > 2, let oldestIndexPath = selectedRows.first {
tableView.deselectRow(at: oldestIndexPath, animated: true)
}
let sirops = listeSirops[indexPath.row]
let objectToRemove = sirops.nom
listeSiropsSelected.remove(object: objectToRemove)
print(listeSiropsSelected)
}

Related

How to pass multiple data in single tableview

Hello I have two different array data that I need to pass into view controller, my ViewControllers design is the same but the only difference is the data. how can I do that? this is my code
var attendance: [GAttendance]!
var subjectAttendances: [GAttendances]!
// In my A controller
let detailAbsenceVC = DetailAbsenceVC()
detailAbsenceVC.attendance = attendances
self.present(detailAbsenceVC, animated: true)
// In my B controller
let detailVC = DetailAbsenceVC()
detailVC.subjectAttendances = subjectAttendances
self.present(detailVC, animated: true, completion: nil)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return attendance.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: GStudentAbsenceCell.cellID, for: indexPath) as! GStudentAbsenceCell
let attendanceItem = attendance[indexPath.row]
cell.configureCell(attendance: attendanceItem)
return cell
}
If you do not distinguish whether you came from A or B, you just need one arrayto store the data in the DetailAbsenceVC, lets call it detailData:
class DetailAbsenceVC : UIViewController {
var detailData = [GAttendance]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return detailData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: GStudentAbsenceCell.cellID, for: indexPath) as! GStudentAbsenceCell
let attendanceItem = detailData[indexPath.row]
cell.configureCell(attendance: attendanceItem)
return cell
}
}
Then, in the A/B controller, just set the detailData:
// In my A controller
let detailAbsenceVC = DetailAbsenceVC()
detailAbsenceVC.detailData = attendances
self.present(detailAbsenceVC, animated: true)
// In my B controller
let detailVC = DetailAbsenceVC()
detailVC.detailData = subjectAttendances
self.present(detailVC, animated: true, completion: nil)

trailingSwipeActionsConfigurationForRowAt not work in stability

I am trying to make swipe-able table view cell for iOS 11 and above and trying to use the trailingSwipeActionsConfigurationForRowAt but it works on iPad and doesn't work on iPhone. It is not called in iPhone.
Surprisingly if I try to swipe the cell more then 10 or 20 times it sometimes works for once.
Here is my controller's table view extension
import UIKit
extension CustomViewController: UITableViewDelegate, UITableViewDataSource {
func initTableView() {
tableView.register(cell: SingleLineListItemViewCell.self)
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 64
tableView.rowHeight = UITableView.automaticDimension
tableView.backgroundColor = .green
tableView.isEditing = false
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = SingleLineListItemViewModel(title: "coko")
let cell = tableView.dequeueReusableCell(withIdentifier: SingleLineListItemViewCell.reuseIdentifier) as? SingleLineListItemViewCell
cell?.model = model
cell?.isFirst = indexPath.row == 0
cell?.isLast = indexPath.row == 2
return cell!
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 64.0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
print("didSelectRowAt....")
router?.routeToTransfer()
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
#available(iOS 11.0, *)
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
return nil
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
// Action here
// In case of delete, you can simply do:
if editingStyle == .delete {
//Remove item at relative position from datasource array
//Reload tableview at the respective indexpath
}
}
#available(iOS 11.0, *)
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { _, _, _ in
//YOUR_CODE_HERE
}
deleteAction.backgroundColor = .red
let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
configuration.performsFirstActionWithFullSwipe = false
return configuration
}
}
Any help would be much appreciated.
The only obvious thing wrong with this is that you're not calling the completion handler. According to the API documentation it is necessary to call the completion handler to indicate whether the operation was successful. What it actually does I have no idea...
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { _, _, completionHandler in
//YOUR_CODE_HERE
completionHandler(true)
}
deleteAction.backgroundColor = .red
let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
configuration.performsFirstActionWithFullSwipe = false
return configuration
}
If this doesn't make any difference, do you have any other gesture recognisers that could be conflicting with the swipe action?
you can try this one
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
return "Delete"
}
// this method handles row deletion
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// remove the item from the data model
// delete the table view row
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Not used in our example, but if you were adding a new row, this is where you would do it.
}
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(
style: .normal,
title: "Delete",
handler: { (action, view, completion) in
//do what you want here
completion(true)
})
action.backgroundColor = .red
let configuration = UISwipeActionsConfiguration(actions: [action])
configuration.performsFirstActionWithFullSwipe = false
return configuration
}
Here is are actions defined as well (point 1 and 2);
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "") { _, _, completionHandler in
// 1. remove object from your array
scannedItems.remove(at: indexPath.row)
// 2. reload the table, otherwise you get an index out of bounds crash
self.tableView.reloadData()
completionHandler(true)
}
deleteAction.backgroundColor = .systemOrange
deleteAction.image = UIImage(named: "trash")
let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
configuration.performsFirstActionWithFullSwipe = true
return configuration
}

didSelectRowAt is not being called for my table view

I want to make the transition from the UITableView cell to the view controller. My didSelectRowAt isn't working.
Here is code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var goals = [String]()
#IBAction func onAddTapped() {
let alert = UIAlertController(title: "Add Goal", message: nil, preferredStyle: .alert)
alert.addTextField { (dessertTF) in
dessertTF.placeholder = "Enter Goal"
}
let action = UIAlertAction(title: "Add", style: .default) { (_) in
guard let goal = alert.textFields?.first?.text else { return }
print(goal)
self.add(goal)
}
alert.addAction(action)
present(alert, animated: true)
}
func add(_ goal: String) {
let index = 0
goals.insert(goal, at: index)
let indexPath = IndexPath(row: index, section: 0)
tableView.insertRows(at: [indexPath], with: .left)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "segue", sender: self)
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return goals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let dessert = goals[indexPath.row]
cell.textLabel?.text = goals
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else { return }
desserts.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
The problem is that you aren't creating your cells correctly.
Inside of your cellForRow function change
let cell = UITableViewCell()
to this:
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
Make sure that in your storyboard file, that you set the cell identifier to "Cell"

Adding swipe button in table view in swift

I have a simple table view showing a list of tasks. I want to show two buttons when user swipes on a cell. A delete button to delete the cell and Completed button to store the task in completed array. I am able to implement the delete button but no idea of showing a second button in the table cell. here is the code.
import UIKit
var taskArray = [String]()
var datesArray = [String]()
class ViewController: UIViewController, UITableViewDataSource
{
#IBOutlet weak var taskTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return taskArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath)
cell.textLabel?.text = "\(indexPath.row + 1). \(taskArray[indexPath.row])"
cell.detailTextLabel?.text = datesArray[indexPath.row]
return cell
}
override func viewDidLoad()
{
super.viewDidLoad()
taskTableView.dataSource = self
let userDefaults = UserDefaults.standard
if let task = userDefaults.stringArray(forKey: "tasks") , let date = userDefaults.stringArray(forKey: "dates")
{
taskArray = task
datesArray = date
}
print(taskArray)
print(datesArray)
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
taskTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// this method handles row deletion
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == .delete
{
// remove the item from the data model
taskArray.remove(at: indexPath.row)
datesArray.remove(at: indexPath.row)
// delete the table view row
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
//function to come back from close button
#IBAction func close(segue: UIStoryboardSegue)
{
}
}
Swift 4.0
You can write below method of tableView to define custom swipe action.
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .default, title: "Delete") { (action, indexPath) in
}
delete.backgroundColor = UIColor.red
let complete = UITableViewRowAction(style: .default, title: "Completed") { (action, indexPath) in
// Do you complete operation
}
complete.backgroundColor = UIColor.blue
return [delete, complete]
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
//self.isEditing = false
print("more button tapped")
}
more.backgroundColor = UIColor.lightGray
let favorite = UITableViewRowAction(style: .normal, title: "Favorite") { action, index in
//self.isEditing = false
print("favorite button tapped")
}
favorite.backgroundColor = UIColor.orange
let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
//self.isEditing = false
print("share button tapped")
}
share.backgroundColor = UIColor.blue
return [share, favorite, more]
}
First make this function return true
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool
{
return true
}
it makes your cell editable , apple provides default deleting and editing options that you can use as like this :
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if(editingStyle == .delete)
{
myArray.remove(at: indexPath.item)
table.deleteRows(at: [indexPath], with: .automatic)
table.reloadData()
}
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
if(studentUser as? String == "Admin")
{
return .delete
}
else
{
return .none
}
}
or you can define your custom ones :
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
{
let del = UITableViewRowAction(style: .normal, title: "Delete")
{
(action, index) in
let alert = FCAlertView()
alert.makeAlertTypeCaution()
alert.cornerRadius = 10
alert.delegate = self
alert.animateAlertInFromBottom = true
alert.animateAlertOutToTop = true
alert.bounceAnimations = true
alert.blurBackground = true
alert.dismissOnOutsideTouch = true
alert.showAlert(inView: self,
withTitle: "Title you want ",
withSubtitle: "Subtitle Comes here",
withCustomImage: nil,
withDoneButtonTitle:"OK" ,
andButtons:["Cancel"])
}
let edit = UITableViewRowAction(style: .default, title: "Edit")
{
(action, index) in
self.view.makeToast("Editing Coming soon...")
}
del.backgroundColor = AppColor.myNewRedColor
edit.backgroundColor = .lightGray
return [edit,del]
}
Swift 4.0
Add Delegate & DataSource
tableView.delegate = self
tableView.dataSource = self
Add DataSource func "canEditRowAt indexPath"
//MARK: - UITableViewDataSource
public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
Add Delegate func "editActionsForRowAt indexPath"
//MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let editAction = UITableViewRowAction(style: .default, title: "Edit", handler: { (action, indexPath) in
//Action edit
print("Action Edit...")
})
editAction.backgroundColor = UIColor.gray //Set button color
let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action, indexPath) in
//Action delete
print("Action Delete...")
})
return [deleteAction, editAction]
}
I hope this helps.
As par your Requirement i have . created Demo for you.
Here is the Output,
When you press Delete element will be removed from Array and when you press Add Button element will be added to new Array.
Here is the link of Demo,
Tableview Demo with Swipable Add and Delete
Step 1:- Connect your Tableview datasource and delegate in Storyboard.
Step 2:- Write DataSource Methods of TableView.
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Preloaded Data"
} else {
return "Added Data to New Array"
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return arrPrelodedData.count
} else {
return arrAddNewData.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SwipeToDelete", for: indexPath) as? SwipeToDelete else {return UITableViewCell()}
if indexPath.section == 0{
cell.lblCellContent.text = arrPrelodedData[indexPath.row] }
else {
cell.lblCellContent.text = arrAddNewData[indexPath.row]
}
return cell
}
//With this we can edit UITableview ex. Swipe to Delete
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if indexPath.section == 0 {
return true } else {
return false
}
}
//Select tableview Editing Style (insert and Delete)-> if custom icon than set None
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.none
}
//Delete Action 1) Create delete Action 2) Remove data with Indexpath 3) fetch data from coredata 4) delete tableview row 4) set delete button background color 5) return deleteAction in arry wether it is single
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
//Destructive Because we want to delete(destroy) the data from tableview
let deleteAction = UITableViewRowAction(style: .destructive, title: "DELETE") { (rowAction, indexpath) in
self.arrPrelodedData.remove(at: indexPath.row)
tableView.deleteRows(at: [indexpath], with: .automatic)
}
let addAction = UITableViewRowAction(style: .normal, title: "ADD 1") { (rowAction, indexpath) in
self.arrAddNewData.append(self.arrPrelodedData[indexPath.row])
tableView.reloadSections(NSIndexSet(index: 1) as IndexSet, with: .none)
// tableView.reloadRows(at: [indexPath], with: .automatic)
}
deleteAction.backgroundColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)
addAction.backgroundColor = #colorLiteral(red: 0.9176470588, green: 0.662745098, blue: 0.2666666667, alpha: 1)
return [deleteAction,addAction]
}
}
I hope this answer will helpful for you.

EditActionsForRowAt not working for two tableView on Scrollview in Swift 3

I use two tableView with 5 and 3 cells over scrollview . Trying to use swipeable gesture on both tableView using editActionsForRowAt method of tableView But it just swipe only three cells of first tableView and other cell swipe by trying 5 to 7 times or unswipeable. Help me to get it ,
Thanks in advance
my code
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == table1{
return 5
}
else if tableView == table2{
return 3
}
else{
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == table1{
let cell = meetingTable.dequeueReusableCell(withIdentifier: "myCell") as! meetingCell
cell.cell_name.text = "abc"
cell.cell_copny.text = "08276"
return cell
}
else{
let cell = followTable.dequeueReusableCell(withIdentifier: "myCell") as! FollowUpCell
cell.cell_name.text = "abc"
cell.cell_phone.text = "134135"
return cell
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let Btn1 = UITableViewRowAction(style: .normal, title: "\u{1F4DE}\n Btn1") { action, index in
//code
}
Btn1.backgroundColor = UIColor.lightGray
let Btn2 = UITableViewRowAction(style: .normal, title: "\u{1F4D3}\n Btn2") { action, index in
//code
}
Btn2.backgroundColor = UIColor.orange
return [Btn2,Btn1]
}

Resources