Good day to all. Faced a problem. I need to make a table with a button and by clicking on the button I get a alert with the number of the cell. The table cells themselves are not active. That's how I realized it. When I scroll the table in the beginning everything is fine, when you press the button, a alert is displayed with the correct line number, but after 4 elements an error appears.
This error appears in the line where I'm working with the 4 tag.
Fatal error: unexpectedly found nil while unwrapping an Optional value
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as UITableViewCell
if (tableView.tag == 1) {
let numLabel: UILabel = tableView.viewWithTag(3) as! UILabel
numLabel.text = String(indexPath.row)
} else if (tableView.tag == 2) {
//Error appears here
let numButton: UIButton = tableView.viewWithTag(4) as! UIButton
numButton.setTitle(String(indexPath.row), for: .normal)
numButton.tag = indexPath.row
}
return cell
}
#IBAction func showAlertForRow(row: UIButton) {
let alert = UIAlertController(title:"Test work",message:"Cell at row \(row.tag) was tapped!",preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
What you are designing for implementing this procedure is not correct. What you can do
Make a custom cell
Add a button in custom cell
Add action in that button in CellForRowAtIndexPath
Handle action from ViewController where you added tableView.
I made a whole project for you.Just to let you know. if you want to add customCell in tableView you need to register it like this in viewDidLoad.
i have done is in ViewController.swift file. check out my project.
let nib = UINib.init(nibName:String(describing: sampleTableViewCell.self) , bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "chatCell")
Then check cellForRowAtIndexPath function:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "chatCell", for: indexPath) as! sampleTableViewCell
cell.clickMeBtn.tag = indexPath.row
cell.clickMeBtn.addTarget(self, action: #selector(onButtonPressed(sender :)), for: .touchUpInside)
return cell
}
Button press function:
func onButtonPressed(sender:UIButton) {
let alert = UIAlertController.init(title:"Cell index is"+String(sender.tag), message: nil, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction.init(title: "ok", style: UIAlertActionStyle.default) { (UIAlertAction) in
}
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
Check only three files:
Github link
ViewController.swift
sampleTableViewCell.swift
sampleTableViewCell.xib**
Here is the output:
Related
I'm developing an iOS app where I want to reload information of a UITableView from a different controller. In order to do that, I have a reference of the controller MainViewController with the UITableView in another controller called (AirlinesController).
I have an issue reloading the data of the UITableView of the first controller where it messes up pretty much:
MainViewController
Each cell of the table you can see leads to AirlinesController:
AirlinesController
So after the user clicks on the "Apply" button, the UITableView of MainViewController reloads using mainView.reloadData(). In this example I would want to see in the MainViewController that the cell with the title "Embraer" has a green label with the text "Completed" and the cell with the title "Airbus" has a yellow label with the title "Employee" but this is the result I get after reloading the table:
Why did the last cell of the table change one of its labels color to yellow?
This is the code I'm using:
MainViewController
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! CellController
let items = self.gameView.data!.levels
cell.model.text = items[indexPath.row].name
if items[indexPath.row].id < self.gameView.game!.level.id {
cell.cost.text = "Complete"
cell.cost.textColor = UIColor.systemGreen
cell.accessoryType = .none
cell.isUserInteractionEnabled = false
} else if items[indexPath.row].id == self.gameView.game!.level.id {
cell.cost.text = "Employee"
cell.cost.textColor = UIColor.systemYellow
cell.accessoryType = .none
cell.isUserInteractionEnabled = false
} else {
cell.cost.text = "\(items[indexPath.row].XP) XP"
}
return cell
}
AirlinesController
#IBAction func apply(_ sender: Any) {
if (mainView.game.XP >= level.XP) {
let new_salary = Int(Float(mainView.game.salary) * level.salaryMultiplier)
let new_XP = Int(Float(mainView.game.XPSalary) * level.XPMultiplier)
mainView.game.salary = new_salary
mainView.game.XPSalary = new_XP
mainView.salaryLabel.text = "\(new_salary) $"
mainView.XPLabel.text = "\(new_XP) XP"
mainView.workingFor.text = "Currently working for \(level.name)"
mainView.game.level = Level(id: level.id, name: level.name, XP: 0, XPMultiplier: 1, salaryMultiplier: 1, aircrafts: [Aircraft]())
mainView.saveGame()
DispatchQueue.main.async {
self.mainView.levelItems.reloadData()
self.navigationController?.popViewController(animated: true)
}
} else {
let alert = UIAlertController(title: "Error", message: "Not enough experience to apply!", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel, handler: nil))
self.present(alert, animated: true)
}
}
How can I fix this?
This is because of the reuse of cells.
Set the default color in else condition.
} else {
cell.cost.textColor = UIColor.gray
cell.cost.text = "\(items[indexPath.row].XP) XP"
}
Another way, you can set the default style property inside the prepareForReuse method of UITableViewCell
class TableViewCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
// Set default cell style
}
}
TableViewCell are reused all the time. My advice is that you have to set the default color every time.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! CellController
cell.cost.textColor = ...your default text color....
...
return cell
I want to display check mark only at selected row as shown in below screenshot.
Here delete and check mark are dynamically created button.
Here my tableview methods below.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if list.count == 0 {
let alertView = UIAlertController(title: "Alert", message: "No layout found. Please add at least one plist file with correct format.", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
self.removeFromSuperview()
}
return list.count
}
var btn = UIButton()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if let cell = cell {
print("cell is available")
// Remove previously created button from reused cell view
if let button = cell.contentView.subviews.first(where: { (view: UIView) -> Bool in
view.isKind(of: UIButton.self)
}) {
button.removeFromSuperview()
}
} else {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
cell?.textLabel?.textAlignment = .left
cell?.textLabel?.text = list[indexPath.row]
if indexPath.row == (list.count - 1) {
cell?.textLabel?.textAlignment = .center
} else {
btn = UIButton(type: UIButtonType.custom) as UIButton
btn.frame = CGRect(x: 146, y: 0, width: 20, height: (cell?.frame.height)!)
btn.addTarget(self, action: #selector(buttonPressed(sender: )), for: .touchUpInside)
btn.tag = indexPath.row
btn.setImage(UIImage(named: "delete.png"), for: .normal)
cell?.contentView.addSubview(btn)
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if list[indexPath.row] == "➕ Add Room"{
print("ADd reoom selrctj")
self.delegate?.enterRoomName()
}else{
self.delegate?.slecetedPicklist(fileName: list[indexPath.row])
self.removeFromSuperview() //hiding drop down
if indexPath.row == btn.tag{ //Here it's not correct i think
btn.setImage(UIImage(named: "check_mark.png"), for: .normal)
}else{
btn.setImage(UIImage(named: "delete.png"), for: .normal)
}
self.table?.reloadData()
}
}
Afetr this implementation i'm not able to achieve this requirement and result is shown below. Even after selecting row it shows delete and this drop down when tap on it i'm removing it from superview.
Can somebody please suggest me how to implement this or where i'm doing wrong?? Thanks in advance.
You need to store (add/remove item or index as per item select and deselect) item or item index in the array and update(Reload) selected row in didSelectRowAt method use following code.
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: false)
And then you need to check in cellForRowAt method if item available in the array then display tick mark accordingly.
Instead of removing the button, use the same button with different icons for selected/normal state. It will show the required icon on same button
Once you select a button, change the selection state of button & reload the table or reload that particular row. The changes will reflect.
My code is this:
let alrController = UIAlertController(title: "Membri", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet)
tableView.backgroundColor = UIColor.white
alrController.view.addSubview(tableView)
let cancelAction = UIAlertAction(title: "Esci", style: UIAlertActionStyle.cancel, handler: {(alert: UIAlertAction!) in})
alrController.addAction(cancelAction)
self.present(alrController, animated: true, completion:{})
I want to populate (but I don't know how) the tableView with this values in my array: names["name1","name2","name3"]
Can someone help me?
To populate an action sheet, you don't add a tableView. Instead, you simply add the actions and it will create and manage the tableView privately.
For a recent tutorial, see UIAlertController Examples
The idea is that you'd create an UIAlertAction for each String in your array, including a closure for what to do when user taps that action row.
for name in names
{
let namedAction = UIAlertAction(title: name, style: .default)
{ (action) in
// do something when this action is chosen (tapped)
}
alrController.addAction(namedAction)
}
You need to implement the following tableView dataSource methods in order to populate this data and also set tableView datasource to alertController like below:-
tableView.dataSource = alertController
TableView DataSource method
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell: yourCustomCell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? yourCustomCell
else {
fatalError("yourCustomCell not found")
}
cell.textLabel.text = names[indexpath.row]
return cell
You can use custom UIViewController transition to achieve this kind of functionality.
This link: https://github.com/pgpt10/Custom-Animator will give you an idea of how you can achieve that.
Well i know these types of questions are answered by SO before, but mine is little different so please read it.
I am using a button inside tableview cell. On click of button i am showing an AlertViewController with list of actions. On selecting action my button text and my custom model class propery is changed.
Button text is changing on button click. But my problem is cell not storing button state. If i change button of 1st cell and then click on second cell button all other buttons back to its default text.
please see this video
Here is my code
setting cell data
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("dbColumns", forIndexPath: indexPath) as! CustomColumnTableViewCell
let column = columns[indexPath.row]
cell.dbColumnName.text = column.name
cell.dbColumnOrder.titleLabel?.text = column.order
cell.dbColumnOrder.tag = indexPath.row
print(columns)
cell.dbColumnOrder.addTarget(self, action: #selector(ViewController.showAlert(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
Action on button click
//MARK:Show Alert
func showAlert(sender:UIButton){
let alert = UIAlertController(title: "Column Order", message: "Please select column order", preferredStyle: .ActionSheet)
let index = sender.tag
let indexPath = NSIndexPath(forRow: index, inSection: 0)
alert.addAction(UIAlertAction(title: "None", style: .Default, handler: { (action) in
//execute some code when this option is selected
self.columns[index].order = "None"
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}))
alert.addAction(UIAlertAction(title: "Assending", style: .Default, handler: { (action) in
//execute some code when this option is selected
self.columns[index].order = "Assending"
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}))
alert.addAction(UIAlertAction(title: "Desending", style: .Default, handler: { (action) in
//execute some code when this option is selected
self.columns[index].order = "Desending"
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
Please hemp me.
In cellForRowAtIndexPath method Set button text like this way
First remove this
cell.dbColumnOrder.titleLabel?.text = column.order
Replace this
cell.dbColumnOrder.setTitle("\(column.order)", for: .normal)
And one more thing you need to increase cell.dbColumnOrder width.
Hope it works
Alright, I am testing around a bit with core data, something I recently have started to discover, basically, what i have so far, is a single view app, that has a data source, and i can press a button and it brings up and alert, which from there i can add names to the list, and delete names from the list, i can close my app and still maintain my data. here is the issue/question, i am trying to do an update, so i can edit names in the list, i have a uibutton set up on my prototype cell, and i have it linked to my viewController, and have a function set inside the IBAction for the button. however, the button does not appear in my sim at run time.
here i have some code.
this is code for the edit button, and its function:
#IBAction func editButton(sender: AnyObject) {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// 2
let alert = UIAlertController(title: "Update",
message: "Please enter the new name.",
preferredStyle: .Alert)
// 3
let updateAction = UIAlertAction(title: "Save",
style: .Default){(_) in
let nameTextField = alert.textFields![0]
self.updateName(indexPath.row, newName: nameTextField.text!)
self.tableView.reloadData()
}
// 4
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alert.addTextFieldWithConfigurationHandler(nil)
alert.addAction(updateAction)
alert.addAction(cancelAction)
// 5
self.presentViewController(alert, animated: true, completion: nil)
}
here is my cellForRowAtIndexPath func
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")!
let people = peoples[indexPath.row]
cell.textLabel!.text = people.name
return cell
}
here is an image of my storyboard
Storyboard
if you need further information or code, please let me know and i will provide it.