I am getting the following error
libc++abi.dylib: terminating with uncaught exception of type NSException
The problem comes when I press the Unbutton on the main storyboard. It does not "show segue" as directed.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
}
My Home storyboard class is:
import UIKit
class Home: UITableViewController {
var array = ["item1, item2", "item3"]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("customcell")
as! TableviewCell
cell.textLabel?.text = array[indexPath.item]
cell.Title = cell.textLabel?.text!
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "datasegue"){
let cell = sender as! TableviewCell
let detailview = segue.destinationViewController as! DetailViewController
detailview.PreTitile = cell.Title
}
}
Related
I'm trying to implement the didSelectRow function and perform a segue but when running the cells select and nothing happens.
I created a print statement that also doesn't run which proves that the function doesn't appear to be firing. Why would this be?
I have checked the identifier is correct and have researched these for a few hours going through many stack overflow threads but with little luck.
import UIKit
import CoreData
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let viewController = ListNameViewController()
let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
var itemChoosen = 0
override func viewDidLoad() {
super.viewDidLoad()
homeListsTableView.delegate = self
homeListsTableView.dataSource = self
viewController.loadList()
}
#IBOutlet weak var homeListsTableView: UITableView!
#IBAction func templatesButton(_ sender: Any) {
tabBarController?.selectedIndex = 2
}
#IBAction func allListsButton(_ sender: Any) {
tabBarController?.selectedIndex = 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewController.listName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let result = viewController.listName[indexPath.row]
cell.textLabel?.text = ("\(String(result.listName!))")
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
context!.delete(viewController.listName[indexPath.row])
viewController.listName.remove(at: indexPath.row)
viewController.saveList()
homeListsTableView.reloadData()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "items2", sender: self)
print("selected")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(false)
viewController.loadList()
homeListsTableView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
homeListsTableView.reloadData()
}
}
ListNameViewController:
import UIKit
import CoreData
class ListNameViewController: UIViewController, UITableViewDelegate {
let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
var listName : [ListName] = []
override func viewDidLoad() {
super.viewDidLoad()
createButtonChange.isEnabled = false
//Objective-C Line used to keep checking if the text field is vaild before enabling the submit button
listNameValue.addTarget(self, action: #selector(textValidation), for: UIControl.Event.editingChanged)
}
#IBOutlet weak var listNameValue: UITextField!
#IBOutlet weak var locationOption: UITextField!
#IBOutlet weak var createButtonChange: UIButton!
#objc func textValidation() {
//Text Field Validation check before button is enabled
if listNameValue.text!.isEmpty {
createButtonChange.isEnabled = false
} else {
createButtonChange.isEnabled = true
}
}
// Create a new List
#IBAction func createButton(_ sender: Any) {
let newList = ListName(context: context!)
newList.listName = listNameValue.text
saveList()
self.navigationController!.popViewController(animated: true)
viewWillAppear(false)
}
func saveList() {
do {
try context!.save()
} catch {
print("Error saving context \(error)")
}
}
func loadList() {
let request : NSFetchRequest<ListName> = ListName.fetchRequest()
do{
listName = try context!.fetch(request)
} catch {
print("Error loading categories \(error)")
}
}
//Pass data to the HomeViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// let vc = segue.destination as! HomeViewController
}
}
// commented out core data and just used a normal array for testing.
Did you add segue in your storyboard for the tableview ? In this case the didSelect is not call but the prepare(for segue) of the tableview controller is called.
Ok solved it - There was a rouge tap gesture recognizer on the page. Removed it and works on one click. I have seen that if you wish to keep the gesture just add this line of code at the top of the function:
tap.cancelsTouchesInView = false
Took three days but I got there. Thanks for the help!
I want send data between UITableViewCell and UIViewController
I have a list of address, in my cell I have a button (edit) and I want to send all the information about the address in the cell
#IBOutlet weak var address: UILabel!
#IBOutlet weak var rfc: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is ContainerVC{
let vc = segue.destination as? ContainerVC
vc.address = self.address.text
}
}
Method does not override any method from its superclass
Let's use segue inside your controller which contains the tableview which uses your UITableViewCell
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueIdentifier,
let destination = segue.destination as? ContainerVC,
let selectedIndex = tableView.indexPathForSelectedRow?.row
{
destination.address = self.addresses[selectedIndex].text
self.navigationController?.pushViewController(destination, animated: true) // For example
}
}
To pass data from cell Class to Controller Class use delegate methods on edit action.
Define call back delegate method in UITableViewCell class:
var callBack: ((NSDictionary) -> (Void))?
Use DS as per your need.
On Edit Action call delegate method and pass the data
#IBAction func editAction(_ sender: Any) {
callBack?(dictionary)
}
In cellForRowAt indexPath call delegate method with cell object
cell.callBack = { (currentData : NSDictionary)
in
// Perform Action on data
}
To Pass Data from Controller class to cell class you can simply define a method in cell class and can call in cellForRowAt indexPath.
Define SetData(dictionary) in cell class
In View Controller
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewOutlet.dequeueReusableCell(withIdentifier: "CellName", for: indexPath) as! CellClassName
cell.SetData(dict)
return cell
}
You need to do this from your UIViewController in which the UITableViewCell is implemented:
Assuming you have an array to populate your UITableView, the code would be something like:
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var addresses = [String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showAddressDetail" {
if let secondViewController = segue.destination as? SecondViewController {
secondViewController.address = sender as! String
}
}
}
}
extension FirstViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.addresses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: YourTableViewCell = tableView.dequeueReusableCell(withIdentifier: "YourTableViewCell", for: indexPath) as! YourTableViewCell
cell.address = self.addresses[indexPath.row]
cell.delegate = self
return cell
}
Since you have to call your function from an action triggered by a button on your UITableViewCell, you can implement a protocol on your cell:
import UIKit
protocol YourTableViewCellDelegate: class {
func selectedAddress(address: String)
}
class YourTableViewCellDelegate: UITableViewCell {
weak var delegate: YourTableViewCellDelegate?
var address: String
}
And in your button action, call your delegate method like this:
#IBAction func selectAddress() {
self.delegate?.selectedAddress(address: self.address)
}
This will trigger the delegate on your UIViewController. To handle the call, don't forget to assign your cell delegate to your view controller and implement your cell delegate in your controller:
extension FirstViewController: YourTableViewCellDelegate {
func selectedAddress(address: String) {
// Do stuff with the selected address
}
}
Issue
I am using Xcode 10, Swift 5. I tried to rename some variables and arrays. I changed them all back to the original name though. After I did this my tab bar button throws an error at AppDelegate: Thread 1: signal SIGABRT
console output:
2019-05-23 12:46:56.511809-0400 DIY Home Repair[1958:326215] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key videoImageView.'
* First throw call stack:
(0x1d8b30518 0x1d7d0b9f8 0x1d8a4dec0 0x1d94b5a00 0x2054fb6fc 0x204d31324 0x1d8a1df38 0x204d2df7c 0x2052753a4 0x104560b80 0x104560d7c 0x20528ea38 0x20528ef38 0x20525b740 0x205278a60 0x2054fbe54 0x1dcfbe1f0 0x1dcfc3198 0x2054e7e88 0x204a3abe4 0x204a35478 0x204a33744 0x2054b8994 0x2054b8c50 0x2054c6758 0x2054c2360 0x2054b86d8 0x204a3b9e8 0x204a3bf7c 0x204a3d210 0x204a20420 0x2054fbe54 0x1dcfbe1f0 0x1dcfc3198 0x1dcf260a8 0x1dcf54108 0x1dcf54cf8 0x1d8ac189c 0x1d8abc5c4 0x1d8abcb40 0x1d8abc354 0x1dacbc79c 0x205073b68 0x10455d750 0x1d85828e0)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Attempting to solve issue
I checked all of my segues, class names - they match controllers. I checked all of the code to ensure all the variables and arrays are accurate. I deleted the segue from the rootTabBarController to the ViewController and reconnected. I commented out all of the code to see if there was something in the viewController that was throwing this error. I cleaned the build folder and restarted Xcode. None of this worked.
Code for the viewController
Keep in mind, I commented this all out and added a blank viewDidLoad func. It still threw the NSExeption.
import UIKit
class ProductViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var videos: [Video] = []
override func viewDidLoad() {
super.viewDidLoad()
videos = Video.createVideoArray()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MasterToDetail" {
let destVC = segue.destination as! DetailViewController
destVC.video = sender as? Video
}
}
}
extension ProductViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let video = videos[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell") as! VideoCell
cell.setVideo(video: video)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let video = videos[indexPath.row]
performSegue(withIdentifier: "MasterToDetail", sender: video)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
Here I am sending secondVC textfield value to firstVC tableview but I am not receiving delegate value in tableview.
Here is my code:
In firstVC:
import UIKit
class CreateBusinessViewController: UIViewController, MyDataSendingDelegateProtocol, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var iteamsArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
}
func sendDataToFirstViewController(myData: String) {
self.iteamsArray.append(myData)
print(iteamsArray)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "addSegue") {
let vc = segue.destination as! CreatePopUpViewController
vc.delegate = self
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return iteamsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BusinessTableViewCell
cell.nameLabel.text = iteamsArray[indexPath.row]
return cell
}
}
In secondVC:
protocol MyDataSendingDelegateProtocol {
func sendDataToFirstViewController(myData: String)
}
class CreatePopUpViewController: UIViewController {
var delegate: MyDataSendingDelegateProtocol?
#IBOutlet weak var addTf: UITextField!
#IBAction func saveButn(_ sender: Any) {
self.delegate?.sendDataToFirstViewController(myData: addTf.text!)
dismiss(animated: true, completion: nil)
}
}
How to add secondVC textfield data to firstVC array?
To Reflect the data to tableview cell you just need to reload the table view like i have updated your function check below code.
func sendDataToFirstViewController(myData: String) {
self.iteamsArray.append(myData)
tableView.reloadData()
print(iteamsArray)
}
Hope this way may help you.
So I have a two page app. The purpose of the app being the user can store expenses. They log a name and amount (attributes) and this data is stored in Expenses (entity). I have figured out how to create core data values, delete and retrieve. I am now working on updating. This will work by the user tapping on a table in the first view (ExpensesViewController) where the expenses are stored and this takes them to the 2nd view (EditExpensesViewController) where they can update the value back into core data. I am stuck on this 'data transfer' between the views.
I am using the storyboard and connected the first view to the second via 'show' set the segue identifier as 'editExpense'. However nothing happens when the table row is tapped. Any idea why it's not working and what I may have missed out? See here for GIF
ExpensesViewController
import UIKit
import CoreData
class ExpensesViewController: UIViewController {
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
var expenses_array = [Expenses]()
var send_array = [Expenses]()
override func viewDidLoad(){
super.viewDidLoad()
retrieveExpenses()
}
func retrieveExpenses(){
let fetchRequest: NSFetchRequest<Expenses> = Expenses.fetchRequest()
do {
let expenses = try PersistenceService.context.fetch(fetchRequest)
self.expenses_array = expenses
self.tableView.reloadData()
} catch {
print(error.localizedDescription )
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "editExpense") {
let secondViewController = segue.destination as! EditExpensesViewController
secondViewController.send_array = send_array
}
}
}
extension ExpensesViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return expenses_array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
cell.textLabel?.text = expenses_array[indexPath.row].name
cell.detailTextLabel?.text = expenses_array[indexPath.row].amount
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
let fetchRequest: NSFetchRequest<Expenses> = Expenses.fetchRequest()
do {
let result = try PersistenceService.context.fetch(fetchRequest)
// Delete from Core Data and remove from the arrays then save
if result.contains(expenses_array[indexPath.row]){
PersistenceService.context.delete(expenses_array[indexPath.row])
expenses_array = expenses_array.filter { $0 != expenses_array[indexPath.row] }
PersistenceService.saveContext()
self.getTotalExpenses()
self.tableView.reloadData()
}
} catch {
print(error.localizedDescription )
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
send_array = [self.expenses_array[indexPath.row]]
self.performSegue(withIdentifier: "editExpense", sender: self)
}
}
EditExpensesViewController
import UIKit
import CoreData
class EditExpensesViewController: UIViewController {
var send_array = [Expenses]() // Defined from the previous view controller
override func viewDidLoad() {
super.viewDidLoad()
print(send_array)
}
}
First of all conform to tableView delegates and dataSource in your viewDidLoad() :
tableView.delegate = self
tableView.dataSource = self
Delete segue from stroyboard and we will present the controller in code using :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "editExpense") {
let secondViewController = segue.destination as! EditExpensesViewController
secondViewController.send_array = send_array
// "someIdentifier" is the identifier of secondController in storyboard
storyboard?.instantiateViewController(withIdentifier: "someIdentifier")
present(secondViewController, animated: true, completion: nil)
}
}
Be aware to put storyboard identifier for second controller in storyboard using attribute inspector
The problem is that your first view controller is the UITableViewDataSource only. That is not enough. It needs to be the UITableViewDelegate too. didSelectRowAt Is a delegate method, not a data source method, and will not be called unless this view controller is the table views delegate and is explicitly declared as conforming to UITableViewDelegate.