How to button highlight and highlight colour is red in swift - swift

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
}

Related

How to disable/enable notifications in tablewiew by using UISwitch in Swift

I am working on a location-based reminder app. I show all reminders that user created on a table view. I have also UISwitch on every cell. I want that UISwitch disables/enables reminders individually, not all notifications. I couldn't figure it out.
extension MainViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
//switch
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = indexPath.row
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
cell.accessoryView = swicthView
let itemToRemove = self.items![indexPath.row]
let notifToRemove: String = itemToRemove.notifID!
return cell
}
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
}
I believe your code is not working because cells are reused and actions on specific cells should be handled from the cell class rather than from ViewController. Move this code into the UITableViewCell code.
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = switchViewTag!
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
and add a new property in the UITableViewCell
weak var switchViewTag: Int?
Modify your cellForRowAt delegate method to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
cell.switchViewTag = indexPath.row
return cell
}

Swiping a UITableViewCell action affects every 4th cell in the table view instead of ONLY the target cell

I have a weird situation where I swipe a cell to grey it out and it greys every 4th or 6th cell instead of only the single cell that was swiped.
The tableview is initialized as follows:
func setupView() {
view.backgroundColor = .white
tableView.register(EntityCell.self, forCellReuseIdentifier: "entityCell")
tableView.separatorStyle = .none
tableView.dataSource = self
tableView.delegate = self
}
Here is my query to get the data:
func getEntities(taxId : String) {
dispatchGroup.enter()
db.collection("Taxonomy").whereField("entityId", isEqualTo: entityId).whereField("status", isEqualTo: 401).getDocuments { (orderSnapshot, orderError) in
if orderError != nil {
self.showError(show: "Error", display: orderError!.localizedDescription)
} else {
self.entitiesArray.append(contentsOf: (orderSnapshot?.documents.compactMap({ (orderDocuments) -> Order in
Order(dictionary: orderDocuments.data(), invoiceId: orderDocuments.documentID, opened: false)!
}))!)
self.dispatchGroup.leave()
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Here are the standard override functions to populate the tableview:
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return entitiesArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "entityCell", for: indexPath) as? EntityCell else { return UITableViewCell() }
let entityRow = entitiesArray[indexPath.row]
cell.selectionStyle = .none
cell.setTaxonomy(entity: entityRow) // Setting up the cell with the array values
return cell
}
Everything is working fine upto this point. And finally here is the override func for swipe action:
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let complete = UIContextualAction(style: .normal, title: "Verified") { (action, view, completionHandler) in
self.db.collection("Taxonomy").document(self.entitiesArray[indexPath.row].entityId).updateData(["status": 411]) { (error) in
if error == nil {
let cell = tableView.cellForRow(at: indexPath) as? EntityCell
cell?.changeStatus(currentEntity: self.entitiesArray[indexPath.row])
}
}
completionHandler(true)
}
complete.image = UIImage(named: "icon_approved")
complete.backgroundColor = UIColor(hex: Constants.Colors.secondary)
let swipe = UISwipeActionsConfiguration(actions: [complete])
return swipe
}
So I swipe right from the trailing edge of the cell and I see the underlying color and icon as expected. And the cell turns grey via this function via a protocol:
extension EntityCell : EntityStatusDelegate {
func changeStatus(currentEntity: EntityObject) {
entityCellBackground.backgroundColor = .systemGray4
}
}
The cell turns grey. And then I scroll down and I see every 4th or 6th cell is grey as well. Any idea what is going wrong? I am pretty flummoxed at this point.
Cells get recycled. You need either configure them completely or overwrite the prepareForReuse function of the cell or give each cell an unique reuseidentifyer so the tableview can recycle them.
(Last option is the worst as it cost a lot more memory)
Option 1:
Just set the backgroundcolor:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "entityCell", for: indexPath) as? EntityCell else { return UITableViewCell() }
let entityRow = entitiesArray[indexPath.row]
cell.selectionStyle = .none
cell.setTaxonomy(entity: entityRow) // Setting up the cell with the array values
cell.entityCellBackground.backgroundColor = (whatever the default color is)
return cell
}

Swift Tableview in isEditing mode doesn't deselect cell

I try to use standard selection in isEditing mode. When I press the first time it's selected, but when I press the second time it stays selected visually. If I change isEditing to false and next time set it true I can't select rows which were selected previously but they don't mark.
How to fix it? isEditing change by button.
enter image description here
'''
class PermanentInternalViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UIGestureRecognizerDelegate {
var index = 0
var test = ["dghffhfh",
"sadasdsa",
"sgfhghgfh"
]
var selectedArray: [IndexPath] = []
#IBOutlet weak var tableViewInternal: UITableView!
#IBOutlet weak var createButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(tableTapped))
self.tableViewInternal.addGestureRecognizer(tap)
self.tableViewInternal.delegate = self
self.tableViewInternal.dataSource = self
self.tableViewInternal.tableFooterView = UIView()
self.tableViewInternal.allowsMultipleSelectionDuringEditing = true
createButton.layer.cornerRadius = 10
if tableViewInternal.isEditing {
createButton.titleLabel!.text = "Add to your Temporary List"
} else {
createButton.titleLabel!.text = "Create Temporary List"
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
test.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "permanentInsideCell1", for: indexPath) as! customCell
cell.tf.delegate = self
// cell.selectionStyle = .none
cell.tf.text = test[indexPath.row]
cell.tf.isUserInteractionEnabled = false
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! customCell
cell.tf.isUserInteractionEnabled = true
cell.selectionStyle = .none
cell.tf.becomeFirstResponder()
index = indexPath.row
if tableViewInternal.isEditing{
cell.tf.isUserInteractionEnabled = false
}
print (cell.isSelected)
if selectedArray.contains(indexPath) {
// it was already selected
selectedArray.remove(at: selectedArray.firstIndex(of: indexPath)!)
print(selectedArray)
} else {
// wasn't yet selected, so let's remember it
selectedArray.append(indexPath)
print(selectedArray)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
print("Deselect")
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
test.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
test[index] = textField.text!
tableViewInternal.reloadData()
// textField.resignFirstResponder()
}
#IBAction func createTempList(_ sender: UIButton) {
tableViewInternal.isEditing = true
createButton.setTitle("Add to your Temporary List", for: .normal)
}
func addAction() {
// create a new row by appending new empty strings
test.append("")
tableViewInternal.reloadData()
}
#objc func tableTapped(tap:UITapGestureRecognizer) {
tap.cancelsTouchesInView = false
let location = tap.location(in: self.tableViewInternal)
let path = self.tableViewInternal.indexPathForRow(at: location)
if let _ = path {
// self.tableView(self.tableViewInternal, didSelectRowAt: indexPathForRow)
} else {
// handle tap on empty space below existing rows however you want
if self.tableViewInternal.isEditing {
self.tableViewInternal.isEditing = false
self.createButton.setTitle("Create Temporary List", for: .normal)
} else {
addAction()
}
}
}
}
'''
Solved.
Problem was in cell.selectionStyle = .none
If remove it and use a clear background color for tableview all is working.

Preventing last cell row being checked after clicking first and vice versa & adding/removing selected rows to array

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
}
}

didSelectRowAt from custom cell do nothing

below code supposed to call usernames from an array and user able to tap on the desired username. call to that array is a success and I can see the usernames but won't able to tap into it. and it don't even print "didSelectRowAt". appreciate your help. thanks.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath) as! MentionUserCell
cell.username!.text = autoComplete[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return autoComplete.count
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let selectedCell: MentionUserCell = tableView.cellForRow(at: indexPath)! as! MentionUserCell
let selectedWord: String = selectedCell.username!.text! + " "
print("didSelectRowAt")
var string: NSString = NSString(string: self.commentTextView.text!)
string = string.replacingCharacters(in: otoCadang.sharedInstance.foundRange, with: selectedWord) as NSString
// change text field value
commentTextView.text = string as? String
// change cursor position
let positionOriginal = commentTextView.beginningOfDocument
if let cursorLocation: UITextPosition = self.commentTextView.position(from: positionOriginal, offset: otoCadang.sharedInstance.foundRange.location + selectedWord.characters.count)
{
self.commentTextView.selectedTextRange = self.commentTextView.textRange(from: cursorLocation, to: cursorLocation)
}
// remove suggestion
self.autoComplete.removeAll()
self.tableView.reloadData()
tableView.isHidden = true
}
Check List
tableview delegate is set properly
tableview selection mode == No Selection, then change to single selection
function name is correct func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
are you using any tap gesture on this view? remove it.
If you are editing UITextView will can't call to `didselectRowAt. Try to get the Your MentionUserCell, IndexPath in UITextView's methods and handle there. Example:
func textViewDidChange(_ textView: UITextView) {
var parentView = textView as UIView
while !(parentView is UITableViewCell) {
parentView = parentView.superview!
}
if let cell: MentionUserCell = parentView as? MentionUserCell {
if let indexPath = self.tableView.indexPath(for: cell) {
let yourIndexPath = indexPath
}
}
//your code
}
Hope it will help you.
When are using two or more cells the didSelectRow method is not working Well. You can use a button in cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifierName", for: indexPath) as! MentionUserCell
cell.username!.text = autoComplete[indexPath.row]
let button = cell.viewWithTag(101) as! UIButton
button.cellclick.addTarget(self, action: #selector(functionName(sender:)), for: .touchDown)
return cell
}
Now you get the indexPath:-
func functionName(sender:UIButton) {
let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableview)
let indexPath = self.tableview.indexPathForRow(at: buttonPosition)
print(indexPath.row)
}