When I try to run my app, I get the error stating:
fatal error: unexpectedly found nil while unwrapping an Optional
value.
Can anybody tell me if its a way to locate the line where the problem is?
Unfortunately, I don't get a red line where the simulator crashes.
I pasted in all of the code, but the problem must have to do with the alert function because it worked fine until I tried to implement that.
import UIKit
var list = ["Visa code: 1234", "Mastercard code: 4321"]
class notesVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var messageLabel: UILabel!
var userMessage = "Sample text"
var theUserText: UITextField?
#IBOutlet weak var tabelView: UITableView!
#IBAction func addItemButton(_ sender: Any) {
let alertController = UIAlertController(title:"title",
message: "message",
preferredStyle: .alert)
alertController.addTextField(configurationHandler: theUserTextFunc)
let okAction = UIAlertAction(title: "OK",
style: .default,
handler: self.okHandler)
let cancleAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(okAction)
alertController.addAction(cancleAction)
self.present(alertController, animated: true)
}
func theUserTextFunc(textField: UITextField){
theUserText = textField
}
func okHandler(alert: UIAlertAction!){
list.append((theUserText?.text)!)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = list[indexPath.row]
return(cell)
}
// Swipe to delete an item
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete{
list.remove(at: indexPath.row)
tabelView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
messageLabel.text = userMessage
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func customInit(userMessage: String) {
self.userMessage = userMessage
}
}
I tried your code. It works flawless except that you missed to reload on table on OkHandler.
So I suspect issue would be with ur IBOutlet or IBAction connections. Check around that...
import UIKit
var list = ["Visa code: 1234", "Mastercard code: 4321"]
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var messageLabel: UILabel!
var userMessage = "Sample text"
var theUserText: UITextField?
#IBOutlet weak var tabelView: UITableView!
#IBAction func addItemButton(_ sender: Any) {
let alertController = UIAlertController(title:"title",
message: "message",
preferredStyle: .alert)
alertController.addTextField(configurationHandler: theUserTextFunc)
let okAction = UIAlertAction(title: "OK",
style: .default,
handler: self.okHandler)
let cancleAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(okAction)
alertController.addAction(cancleAction)
self.present(alertController, animated: true)
}
func theUserTextFunc(textField: UITextField){
theUserText = textField
}
func okHandler(alert: UIAlertAction!){
list.append((theUserText?.text)!)
self.tabelView.reloadData()
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = list[indexPath.row]
return(cell)
}
// Swipe to delete an item
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete{
list.remove(at: indexPath.row)
tabelView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
messageLabel.text = userMessage
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func customInit(userMessage: String) {
self.userMessage = userMessage
}
}
Try changing your add code
alertController.addTextField(configurationHandler: theUserTextFunc)
to -
alertController.addTextField(configurationHandler: {(textField : UITextField!) -> Void in
textField.placeholder = "Search"
// Set other textfield values that you want
})
Related
This question already has answers here:
Get button click inside UITableViewCell
(18 answers)
Closed 3 years ago.
I have a UITableView list with a button which says "Click Me!". I tried following this answer below: https://stackoverflow.com/a/53043358/7746248 to tie the button to an action, but that didn't work for unknown reasons.
I have checked other ways to tie a button to an event, I have had no luck.
import UIKit
class SampleTableViewCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var button: UIButton!
var tapCallback: (() -> Void)?
#IBAction func didTap(_ sender: Any) {
tapCallback?()
}
}
class TableViewController: UITableViewController {
var tableArray = ["New York", "Chicago", "North Island"]
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
self.clearsSelectionOnViewWillAppear = false
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.tableArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SampleCell", for: indexPath) as! SampleTableViewCell
// Configure the cell...
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let names = self.tableArray[indexPath.row]
cell.name.text = names
cell.tapCallback = {
// do stuff
DispatchQueue.main.async {
let alert = UIAlertController(title: "title", message: "Button Clicked!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
self.present(alert, animated: true)
}
}
return cell
}
}
Any other simple way to do this?
Add target inside cellForRowAt like below
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SampleCell", for: indexPath) as! SampleTableViewCell
// Configure the cell...
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let names = self.tableArray[indexPath.row]
cell.name.text = names
cell. button.addTarget(self, action: #selector(alertMethod), for: .touchUpInside)
return cell
}
#objc fileprivate func alertMethod() {
let alert = UIAlertController(title: "title", message: "Button Clicked!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
self.present(alert, animated: true)
}
}
I've managed to add data to a table view and import that to the specific Firebase location. I now want to be able to delete this data from inside the application.
Currently, with this code the entry successfully deletes from within the app, but not Firebase and when the app is reopened the entry returns. Basically I would like to know what I've done wrong and what I can change to actually delete the entry from the real time database.
import UIKit
import Firebase
class WorkoutNotesViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
var addExer:[String] = []
var handle: DatabaseHandle?
var ref: DatabaseReference?
var keyArray: [String] = []
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var exerView: UITextField!
#IBAction func inputButton(_ sender: Any) {
if exerView.text != ""
{
ref?.child("users").child(Auth.auth().currentUser!.uid).child("notes").childByAutoId().setValue(exerView.text)
exerView.text = ""
}
else
if exerView.text == "" {
let alertController = UIAlertController(title: "Error", message: "Retry", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return addExer.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = addExer[indexPath.row]
return cell
}
// Do any additional setup after loading the view.
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelectionDuringEditing = true
ref = Database.database().reference()
handle = ref?.child("users").child(Auth.auth().currentUser!.uid).child("notes").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String
{
self.addExer.append(item)
self.tableView.reloadData()
}
})
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
print(indexPath.row)
if editingStyle == .delete {
GetAllKeys()
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when, execute: {
self.ref?.child("users").child(Auth.auth().currentUser!.uid).child("notes").child(self.keyArray[indexPath.row])
self.addExer.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
self.keyArray = []
})
}
}
func GetAllKeys() {
ref?.child("users").child(Auth.auth().currentUser!.uid).child("notes").observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
self.keyArray.append(key)
}
})
}
}
You have to embed removeValue()
self.ref?.child("users").child(Auth.auth().currentUser!.uid).child("notes").child(self.keyArray[indexPath.row]).removeValue()
Also it's better to use a completion to be 100% sure that the item is deleted from firebase
self.ref?.child("users").child(Auth.auth().currentUser!.uid).child("notes").child(self.keyArray[indexPath.row]).removeValueWithCompletionBlock({ (error, refer) in
if error != nil {
print(error)
} else {
print("Child Removed successfilly")
}
I have a tableview with buttons, and I would like to create a UIActionSheet here when I click on the 3 dots button. It is a custome tableview cell.
My UITableViewCell:
import UIKit
class UserTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func view(with user: User){
nameLabel.text = user.getName();
}
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
}
#IBAction func btnDial(_ sender: UIButton) {
}
}
and in my view controller:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as? UserTableViewCell;
cell?.view(with: users[indexPath.row]);
return cell!;
}
Try this and do some changes in UserTableViewCell
class UserTableViewCell: UITableViewCell {
weak var myVC : UIViewController?
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
let actionsheet = UIAlertController(title: nil, message: nil, preferredStyle: UIAlertControllerStyle.actionSheet)
actionsheet.addAction(UIAlertAction(title: "Take a Photo", style: UIAlertActionStyle.default, handler: { (action) -> Void in
}))
actionsheet.addAction(UIAlertAction(title: "Choose Exisiting Photo", style: UIAlertActionStyle.default, handler: { (action) -> Void in
}))
actionsheet.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { (action) -> Void in
}))
myVC?.present(actionsheet, animated: true, completion: nil)
}
}
And modify this method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as? UserTableViewCell;
cell?.view(with: users[indexPath.row]);
cell?.myVC = self
return cell!;
}
make outlets of button in cell class
then in tableView where you are using this cell write the code below in cellForRowAtIndexPath
cell.yourButton.addTarget(self, action: #selector(yourButtonTapped), for: .touchDown)
and now in your yourButtonTapped method write actionSheet code following the link :
UIActionSheet iOS Swift
hope its help
Closure Approach
1 - Declare your actionBlock in your UserTableViewCell
var actionClosure : (()->Void)? = nil
2 - Execute your action block in your Cell Action
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
self.actionClosure?()
}
3 - Setup your cell block action adjusting your cellForRowAtIndexPath method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserTableViewCell", for: indexPath) as! UserTableViewCell
cell.actionClosure = { [weak self] in
//SHow your ActionSheet Here
}
return cell
}
Full Code
CellForRow implementation
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserTableViewCell", for: indexPath) as! UserTableViewCell
cell.actionClosure = { [weak self] in
//SHow your ActionSheet Here
}
return cell
}
TableView Cell
import UIKit
class UserTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
var actionClosure : (()->Void)? = nil
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func view(with user: User){
nameLabel.text = user.getName();
}
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
self.actionClosure?()
}
#IBAction func btnDial(_ sender: UIButton) {
}
}
just add onMenubtnClick method in your ViewControler instead of cell.
add this in your cellForRowAt method
cell.youtBtn.addTarget(self, action: #selector(self.btnMenu(_:)), for: .touchUpInside)
add this code in your ViewController
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
}
I'm trying to make a pros and cons list in swift, but whenever I delete a con it deletes a pro. I think that it is a problem with index path being linked to both the pros and cons view controller but I don't know how or where I can separate them
class prosConsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
#IBOutlet var prosTableViewOutlet: UITableView!
#IBOutlet var consTableViewOutlet: UITableView!
#IBOutlet var tableViewOutlet: UITableView!
var colleges : [NetCollege] = []
#IBOutlet var consTableView: UITableView!
var collegesTwo : [NetCollegeTwo] = []
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView == tableViewOutlet
{
return colleges.count
}
else
{
return collegesTwo.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
if tableView == tableViewOutlet
{
let cell = tableViewOutlet.dequeueReusableCellWithIdentifier("cellID") as! tableViewCell
//the line under maybe?
let college = colleges[indexPath.row]
cell.textLabel?.text = college.name
return cell
}
else
{
let cellTwo = consTableView.dequeueReusableCellWithIdentifier("IDCell") as! tableViewCell
let collegeTwo = collegesTwo[indexPath.row]
cellTwo.textLabel?.text = collegeTwo.conName
return cellTwo
}
}
override func viewDidLoad()
{
super.viewDidLoad()
editButtonItem().tag = 0
func shouldAutorotate() -> Bool {
return false
}
func supportedInterfaceOrientations() -> Int {
return UIInterfaceOrientation.LandscapeRight.rawValue
}
}
#IBAction func plusButtonTwo(sender: UIBarButtonItem)
{
let alertTwo = UIAlertController(title: "Add Con", message: nil, preferredStyle: .Alert)
alertTwo.addTextFieldWithConfigurationHandler
{ (textField) -> Void in
textField.placeholder = "Add Con Here"
}
let cancelActionTwo = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alertTwo.addAction(cancelActionTwo)
let addActionTwo = UIAlertAction(title: "Add", style: .Default) { (action) -> Void in
let addCollegesTextFieldTwo = (alertTwo.textFields?[0])! as UITextField
let netCollegeTwo = NetCollegeTwo(nameTwo: addCollegesTextFieldTwo.text!)
self.collegesTwo.append(netCollegeTwo)
self.consTableView.reloadData()
}
alertTwo.addAction(addActionTwo)
self.presentViewController(alertTwo, animated: true, completion: nil)
}
#IBAction func onTappedPlusButton(sender: UIBarButtonItem)
{
let alert = UIAlertController(title: "Add Pro", message: nil, preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler
{ (textField) -> Void in
textField.placeholder = "Add Pro Here"
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(cancelAction)
let addAction = UIAlertAction(title: "Add", style: .Default) { (action) -> Void in
let addCollegesTextField = (alert.textFields?[0])! as UITextField
let netCollege = NetCollege(name: addCollegesTextField.text!)
self.colleges.append(netCollege)
self.tableViewOutlet.reloadData()
}
alert.addAction(addAction)
self.presentViewController(alert, animated: true, completion: nil)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
{
if editingStyle == UITableViewCellEditingStyle.Delete
{
colleges.removeAtIndex(indexPath.row)
tableViewOutlet.reloadData()
}
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool
{
return true
}
If you want to implement all this in one view controller, you can try this:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
{
if editingStyle == UITableViewCellEditingStyle.Delete
{
if tableView == tableViewOutlet
{
colleges.removeAtIndex(indexPath.row)
tableView.reloadData()
}
else
{
collegesTwo.removeAtIndex(indexPath.row)
tableView.reloadData()
}
}
}
But in this case better solution would be to create two classes called like DataSourceOne, DataSourceTwo (or TableViewModelOne, TableViewModelTwo), and implement all related logic there. This even could be two instances of just one class DataSource, depending on what exactly you need. Then you can instantiate those helper classes in viewDidLoad and assign them to dataSource and delegate properties of your table views. Your will also need to hold strong reference for them somewhere, because dataSource and delegate properties are week.
I have an issue that prevents me from display the input from the alert message into the table view. There are no errors but I don't know what's the real problem that makes the table empty.
import UIKit
import Parse
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource{
#IBOutlet weak var childTbl: UITableView!
var names=[String] ()
override func viewDidLoad() {
super.viewDidLoad()
self.childTbl.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
#IBAction func Addchild(sender: AnyObject) {
var alert=UIAlertController(title: "",
message: "إضافة طفل جديد",
preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler({(textField) -> Void in
textField.text=""
})
alert.addAction((UIAlertAction(title: "حفظ", style: .Default, handler: { (ACTION) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
if let textField = alert.textFields!.first as?UITextField{
self.names.append(textField.text)
println(self.names)
}
})))
alert.addAction((UIAlertAction(title: "إلغاء", style: .Default, handler: { (ACTION) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
})))
//to display the alert
presentViewController(alert,
animated: true,
completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//UITabledatasource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return self.names.count;
}
//function to creat a cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
var cell:UITableViewCell = self.childTbl.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
cell.textLabel?.text = self.names[indexPath.row]
return cell
}
}