Nil Error While Running - swift3

i am working on TODO app,it was all completed and was running well but suddenly it begins to give an error "fatal error: unexpectedly found nil while unwrapping an Optional value".Need Some Guide!
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var tableView: UITableView!
var tasks : [Task] = [ ]
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
getdata()
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let task = tasks[indexPath.row]
if task.isimportant{
cell.textLabel?.text = " ★ \(task.name!)"
}else{
cell.textLabel?.text = task.name!
}
return cell
}
func getdata() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do{
tasks = try context.fetch(Task.fetchRequest())
}
catch {
print ("Failed!")
}
}
}

You should always avoid unwrapping optionals with an ! because of the danger of encountering a runtime error if the optional is missing. Try the following:
let taskName = task.name ?? "No name"
if task.isimportant{
cell.textLabel?.text = " ★ \(taskName)"
}else{
cell.textLabel?.text = taskName
}

Related

How to get selected index of items in more navigation controller

I use below code for get selected items of tab bar controller. My UITabbar has 7 view controllers(there are 3 items in More tab).
this code work only for 5 tabs but it don`t return selected index of items on More!
import UIKit
class CustomTabbarController: UITabBarController{
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(self.selectedIndex)
}
}
Following code worked for me. I had to redesign moreTableView to follow my app design. Function 'didSelectRowAt' returns what index is selected.
This code is added to 'UITabBarController' class.
var moreTableView: UITableView?
weak var currentTableViewDelegate: UITableViewDelegate?
func customizeMoreTableView() {
moreTableView = moreNavigationController.topViewController?.view as? UITableView
currentTableViewDelegate = moreTableView?.delegate
moreTableView?.delegate = self
moreTableView?.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (viewControllers?.count ?? 4) - 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let moreCell = UITableViewCell()
let item = viewControllers?[indexPath.row + 4].tabBarItem
moreCell.textLabel?.text = item?.title
moreCell.textLabel?.textColor = .white
moreCell.imageView?.image = item?.image
moreCell.imageView?.tintColor = .white
moreCell.backgroundColor = .black
return moreCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentTableViewDelegate?.tableView!(tableView, didSelectRowAt: indexPath)
}
Get selected item like this :
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(tabBar.items?.index(of: item))
}

Swift 3.1 Label in TableViewCell - unexpectedly found nil

I'm trying to create two tableViews in one UIViewController. But when I'm trying to assign value to UILabel, getting an error: fatal error: unexpectedly found nil while unwrapping an Optional value
I wonder why, I have almost the same code for TableViewController with one tableView and it works with no issues. It looks like these UI Labels are not initialized when trying to assign value to it. But don't understand how to fix it.
It fails here:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell?
if tableView == self.guestsTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "guestCell", for: indexPath) as! GuestAtTableTableViewCell
if let guestsTable = guestsTableFetchedResultsController?.object(at: indexPath) {
print(guestsTable.guestName) // works fine, prints the value
print(cell.guestNameLabel.text) //fails here with error fatal error: unexpectedly found nil while unwrapping an Optional value
cell.guestNameLabel.text = guestsTable.guestName
cell.openTimeLabel.text = String(describing: guestsTable.openTime)
cell.cellDelegate = self
}
}
else if tableView == self.ordersTableView {
cell = tableView.dequeueReusableCell(withIdentifier: "orderCell", for: indexPath)
//to be done
}
// Configure the cell...
return cell!
}
Full code of this class:
import UIKit
import CoreData
class TableUIViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, CellWithButtonDelegate {
//The following two variables will not be nil because prepare for segue will set them
var tableName: String?
var table: TablesTable? = nil
fileprivate var currentTableSession: TableSessionTable? {
get {
let tableSessionTable = TableSessionTable()
return tableSessionTable.getCurrentTableSession(table: table!)
}
}
fileprivate var guestsTableFetchedResultsController: NSFetchedResultsController<GuestsTable>?
fileprivate var ordersTableFetchedResultsController: NSFetchedResultsController<OrdersTable>?
#IBOutlet weak var tableNameLabel: UILabel!
#IBOutlet weak var tableCapacityLabel: UILabel!
#IBOutlet weak var tableCountOfGuestsLabel: UILabel!
#IBOutlet weak var tableDescriptionTextView: UITextView!
#IBAction func closeTableButtonPressed(_ sender: UIButton) {
}
#IBOutlet weak var guestsTableView: UITableView!
#IBOutlet weak var ordersTableView: UITableView!
#IBAction func addGuestButtonPressed(_ sender: UIButton) {
let guestsTable = GuestsTable()
let tablesTable = TablesTable()
let table = Table(tableName: tableName!, tableCapacity: 0, locationX: nil, locationY: nil, tableImage: nil)
try? guestsTable.addNewGuest(table: tablesTable.getOrCreateTable(table: table))
updateUI()
}
#IBAction func addOrderButtonPressed(_ sender: UIButton) {
}
override func viewDidLoad() {
guestsTableView.dataSource = self
guestsTableView.delegate = self
guestsTableView.register(GuestAtTableTableViewCell.self, forCellReuseIdentifier: "guestCell")
ordersTableView.dataSource = self
ordersTableView.delegate = self
ordersTableView.register(UITableViewCell.self, forCellReuseIdentifier: "orderCell")
updateUI()
}
func didPressButton(table: TablesTable) {
}
private func updateUI () {
let tableView = guestsTableView
let context = AppDelegate.viewContext
let request : NSFetchRequest<GuestsTable> = GuestsTable.fetchRequest()
request.predicate = NSPredicate(format: "table= %#", currentTableSession!)
request.sortDescriptors = [NSSortDescriptor(key: "guestName", ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))]
guestsTableFetchedResultsController = NSFetchedResultsController<GuestsTable>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
try? guestsTableFetchedResultsController?.performFetch()
tableView?.reloadData()
}
private func updateUI1 () {
let tableView = ordersTableView
let context = AppDelegate.viewContext
let request : NSFetchRequest<OrdersTable> = OrdersTable.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "menuItem", ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))]
ordersTableFetchedResultsController = NSFetchedResultsController<OrdersTable>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
try? ordersTableFetchedResultsController?.performFetch()
tableView?.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell?
if tableView == self.guestsTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "guestCell", for: indexPath) as! GuestAtTableTableViewCell
if let guestsTable = guestsTableFetchedResultsController?.object(at: indexPath) {
print(guestsTable.guestName) // works fine, prints the value
print(cell.guestNameLabel.text) //fails here with error fatal error: unexpectedly found nil while unwrapping an Optional value
cell.guestNameLabel.text = guestsTable.guestName
cell.openTimeLabel.text = String(describing: guestsTable.openTime)
cell.cellDelegate = self
}
}
else if tableView == self.ordersTableView {
cell = tableView.dequeueReusableCell(withIdentifier: "orderCell", for: indexPath)
//to be done
}
// Configure the cell...
return cell!
}
func numberOfSections(in tableView: UITableView) -> Int {
if tableView == self.guestsTableView {
return guestsTableFetchedResultsController?.sections?.count ?? 1
}
else if tableView == self.ordersTableView {
return ordersTableFetchedResultsController?.sections?.count ?? 1
}
else {return 1}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.guestsTableView {
if let sections = guestsTableFetchedResultsController?.sections, sections.count > 0 {
return sections[section].numberOfObjects
}
else {
return 0
}
}
else if tableView == self.ordersTableView {
if let sections = ordersTableFetchedResultsController?.sections, sections.count > 0 {
return sections[section].numberOfObjects
}
else {
return 0
}
}
else {return 0}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if tableView == self.guestsTableView {
if let sections = guestsTableFetchedResultsController?.sections, sections.count > 0 {
return sections[section].name
}
else {
return nil
}
}
else if tableView == self.ordersTableView {
if let sections = ordersTableFetchedResultsController?.sections, sections.count > 0 {
return sections[section].name
}
else {
return nil
}
}
else {return nil}
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
if tableView == guestsTableView {
return guestsTableFetchedResultsController?.sectionIndexTitles
}
else {
return ordersTableFetchedResultsController?.sectionIndexTitles
}
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
if tableView == guestsTableView {
return guestsTableFetchedResultsController?.section(forSectionIndexTitle: title, at: index) ?? 0
}
else if tableView == ordersTableView {
return ordersTableFetchedResultsController?.section(forSectionIndexTitle: title, at: index) ?? 0
}
else {return 0}
}
}
And full code of UITableViewCell class:
import UIKit
class GuestAtTableTableViewCell: UITableViewCell {
weak var cellDelegate: CellWithButtonDelegate?
#IBOutlet weak var guestNameLabel: UILabel!
#IBOutlet weak var openTimeLabel: UILabel!
#IBAction func didPressButton(_ sender: UIButton) {
}
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
}
}
I guess you have a xib for your UITableViewCell register the xib instead of the class.
Use the following:
guestsTableView.register(UINib.init(nibName: "GuestAtTableTableViewCell", bundle: nil), forCellReuseIdentifier: "guestCell")
As you have created a prototype cell in the storyboard itself you should select the cell in the storyboard and set its identifier there. Next remove the register line from your code for guestCell. It should work

Can't get Cell Swipe Actions to work

It's been awhile since I've made a fool of myself here, so here goes again as I'm still trying to learn and feel as though I'm chasing my tail on this project.
With that said, ultimately I would like to get Jerkoch's SwipeCellKit to work, but not necessary at this point as I just need to figure out how to add more options other than just the 'delete' cell action.
This is what I've got: My Project
This is what I'd like: Jerkoch's Project
I loose my data if I set the delegate property on tableView from
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentTableViewCell
to
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! SwipeTableViewCell
Here is my CommentViewController Code:
import UIKit
import SwipeCellKit
class CommentViewController: UIViewController {
var postId: String!
var comments = [Comment]()
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect.zero)
tableView.separatorInset = UIEdgeInsets.zero
tableView.dataSource = self
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
loadComments()
}
func loadComments() {
Api.Post_Comment.REF_POST_COMMENTS.child(self.postId).observe(.childAdded, with: {
snapshot in
Api.Comment.observeComments(withPostId: snapshot.key, completion: {
comment in
self.fetchUser(uid: comment.uid!, completed: {
self.comments.append(comment)
self.tableView.reloadData()
})
})
})
}
func fetchUser(uid: String, completed: #escaping () -> Void ) {
Api.User.observeUser(withId: uid, completion: {
user in
self.users.append(user)
completed()
})
}
}
extension CommentViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentTableViewCell// Can't update this to SwipeTableViewCell - Will loose data
cell.selectedBackgroundView = createSelectedBackgroundView()
let comment = comments[indexPath.row]
let user = users[indexPath.row]
cell.comment = comment
cell.user = user
cell.delegate = self
return cell
}
}
extension CommentViewController: SwipeTableViewCellDelegate{
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
// handle action by updating model with deletion
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete")
let flagAction = SwipeAction(style: .destructive, title: "Flag") { action, indexPath in
// handle action
}
deleteAction.image = UIImage(named: "flag")
let blockAction = SwipeAction(style: .destructive, title: "Block") { action, indexPath in
// handle action
}
deleteAction.image = UIImage(named: "block")
return [deleteAction, flagAction, blockAction]
}
}
I've even tried something like the following:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
// THIS GIVES THE DEFAULT 'DELETE' ACTION
print("Commit editingStyle")
}
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
print("editActionsForRowAtIndexPath")
let blockAction = UITableViewRowAction(style: UITableViewRowActionStyle.normal, title: "Block", handler: { (action:UITableViewRowAction, IndexPath:IndexPath) in
//self.isEditing = false
print("Block action Pressed")
})
let flagAction = UITableViewRowAction(style: UITableViewRowActionStyle.normal, title: "Flag", handler: { (action:UITableViewRowAction, IndexPath:IndexPath) in
print("Flag action Pressed")
})
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.destructive, title: "Delete", handler: { (action:UITableViewRowAction, IndexPath:IndexPath) in
print("Delete action Pressed")
})
return [blockAction, flagAction, deleteAction]
}
No matter what I do, I can't get more than just the generic 'Delete' action to show on a swipe action.
I don't even think the following function is being called/referenced:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
What am I doing wrong, or what do I need to CUD to get 3 swipe actions; Delete, Flag and Block, to make Apple happy for my next submission.
Thank you all in advance!
This question is outdated but i'll write the answer which i think would have done the trick :)
You should be using UITableViewController not UIViewController for your view. SwipeCellKit only works with TableViewControllers.

Permanent storage To-Do App Swift

I've got a simple to do list app, and I would like the items in the list to appear after the app is closed. What I have so far does not do it, not sure if everything is in the right place?
Code in my first ViewController:
var list = [String]()
class FirstViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var myTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = list[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
list.remove(at: indexPath.row)
myTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
if let x = UserDefaults.standard.object(forKey: "cell") as? [String]! {
list = x
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Code in my second ViewController:
class SecondViewController: UIViewController {
#IBOutlet weak var input: UITextField!
#IBAction func addItemButton(_ sender: AnyObject) {
list.append(input.text!)
input.text = ""
UserDefaults.standard.set(input.text, forKey: "cell")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You are reloading the tableview at viewDidAppear before getting the data. Get your data from user defaults first (set list) and then reload the table view.
Hint:-
override func viewDidAppear(_ animated: Bool) {
if let x = UserDefaults.standard.object(forKey: "cell") as? [String]! {
list = x
myTableView.reloadData()
}
}

Issue with "didSelectRowAt indexPath" in Swift 3

I'm pretty new to swift but I've managed to follow along for the most part. However, there's an issue with my code that the code itself can't even identity apparently(I'm not receiving any error signs). I'm trying to click on a row from the table view to make it go to the following page but I don't think the code is recognizing the didselectrow method. Maybe one of you more experienced persons can help me out.
Here's the code:
import UIKit
import Foundation
class OnePercentersViewController: UIViewController, UITableViewDataSource
{
var copiedExecutiveArray : [String] = NSArray() as! [String]
var identities : [String] = NSArray() as! [String]
override func viewDidLoad()
{
super.viewDidLoad()
let copyExecutiveNames : ExecutiveArray = ExecutiveArray()
copiedExecutiveArray = copyExecutiveNames.executiveNames
identities = copyExecutiveNames.executiveNames
}
//how many sections in table
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
//returns int (how many rows)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return copiedExecutiveArray.count
}
//contents of each cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")//UITableViewCell()
let personName = copiedExecutiveArray[indexPath.row]
cell?.textLabel?.text = personName
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let execName = identities[indexPath.row]
let viewController = storyboard?.instantiateViewController(withIdentifier: execName)
self.navigationController?.pushViewController(viewController!, animated: true)
print("button clicked")
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
}
I haven't checked other parts of your code, but to make tableView(_:didSelectRowAt:) work, your ViewController needs to conform to UITableViewDelegate:
class OnePercentersViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
Also you need to connect the delegate of the UITableView to your ViewController in your storyboard. Or you can do it in code, if you have an #IBOutlet to the UITableView. Anyway, not only dataSource, but also delegate.
And this is not a critical issue, but you can write the first two lines of the class as:
var copiedExecutiveArray : [String] = []
var identities : [String] = []
After spending a day and a half watching Youtube videos and reading comments to similar problems, tt took trial and error but I finally got it thanks to you guys!It finally clicked when I read that I had to connect the delegate to the ViewController through the storyboard, so thanks for that!
Here's the final code for those with similar issue:
import UIKit
import Foundation
class OnePercentersViewController: UIViewController,UITableViewDataSource, UITableViewDelegate
{
var copiedExecutiveArray : [String] = []
var identities : [String] = []
override func viewDidLoad()
{
super.viewDidLoad()
let copyExecutiveNames : ExecutiveArray = ExecutiveArray()
copiedExecutiveArray = copyExecutiveNames.executiveNames
identities = copyExecutiveNames.executiveNames
}
//how many sections in table
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
//returns int (how many rows)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return copiedExecutiveArray.count
}
//contents of each cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let personName = copiedExecutiveArray[indexPath.row]
cell.textLabel?.text = personName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let execName = identities[indexPath.row]
let viewController = storyboard?.instantiateViewController(withIdentifier: execName)
self.navigationController?.pushViewController(viewController!, animated: true)
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
}