Swift nil coalescing operator with array - arrays

I am trying out an exercise in creating a simple todo list.
Before introducing Realm or coreData i wanted to test it out and see if everyting is going smoothly.
I know i probably can make this work with some if conditions but i would love to be able to use the nil coalescing operator (i just love the simplicity of it), and i am not sure why its not working.
I made it work without it, but really interested what is the reason it is behaving like this.
When i launch the app it just shows "No Category Added" even after i add some items to the array and print them out, list stays the same.
import UIKit
class CategoriesTableView: UITableViewController {
var testData = [FauxData]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
}
// MARK: - Data Methods
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let data = testData[indexPath.row].categoryTitle ?? "No Category Added"
cell.textLabel?.text = data
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testData.count
}
#IBAction func addItem(_ sender: UIBarButtonItem) {
CreateNewItem(item: "test")
tableView.reloadData()
}
func CreateNewItem(item: String) {
let newItem = FauxData()
newItem.categoryTitle = item
testData.append(newItem)
print(item)
}
}
This is the class FauxData:
class FauxData {
var categoryTitle: String?
}
Sry if this is too simple or a duplicate, i wasn't able to find and appropriate answer.

Unfortunately, indexing an empty array crashes instead of returning nil, so you can't use the nil coalescing operator. Instead, use the .isEmpty property along with the ?: operator to achieve your goal:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let data = testData.isEmpty ? "No Category Added" : testData[indexPath.row].categoryTitle
cell.textLabel?.text = data
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testData.isEmpty ? 1 : testData.count
}
Note: You have to return 1 from tableView(_:numberOfRowsInSection:) when the array is empty so that tableView(_:cellForRowAt:) will be called to return your default message.
If you implement safe array indexing, you could then use the nil coalescing operator:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let data = testData[safe: indexPath.row]?.categoryTitle ?? "No Category Added"
cell.textLabel?.text = data
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testData.isEmpty ? 1 : testData.count
}

Related

Following tutorial hacking with swift, coded exact same thing, but in the output console, my output is coming this "[ ]"

when I tried to use this code to get the images, I'm not getting list list of images
import UIKit
class ViewController: UITableViewController {
var pictures = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let fm = FileManager.default
let path = Bundle.main.resourcePath!
let items = try! fm.contentsOfDirectory(atPath: path)
for item in items {
if item.hasPrefix("nssl"){
//load pics
pictures.append(item)
}
}
print(pictures)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictures.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)
cell.textLabel?.text = pictures[indexPath.row]
return cell
}
}

How do I can upload label under the sections by using footer in the TableView in the Swift?

StackoverFlow.
I understand those footer may* have many method to make a label under the cells as known footer.
I know how to create footer with index.row which allow you automatic title, image, and everything with the Arrays, otherwise footer don't?
I am try to make the title under the sections like setting style in iOS 13.
resource code:
When you create an arrays for giving automatic cells.
class SettingViewController: UIViewController {
var settingtitle = ["Item1", "Item2", "Item3"]
var footerTitle = ["Footer1", Footer2", Footer3"]
#IBOutlet weak var TableView: UITableView!
}
Now It's time to build UITableView with Delegate and Data Source.
extension SettingViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return settingtitle.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = TableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath) as? SettingTableViewCell
cells?.Setting_Title.text = settingtitle[indexPath.row]
return cells!
}
}
But How could I uploading the label in the under cells with the array?
I tried many method around stack overflow, but I noticed one of their method has worked but it's show under one footer for whole cells.
thanks you for your helps thought this issues.
Let me know if you know.
implement this method:
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return footerTitle[section]
}
=== edited ===
If you want to set footer for each cell:
func numberOfSections(in tableView: UITableView) -> Int {
return settingtitle.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = TableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath) as? SettingTableViewCell
cells?.Setting_Title.text = settingtitle[indexPath.section]
return cells!
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return footerTitle[section]
}
First set delegate:
TableView.delegate = self
Then Add below method:
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 100))
footerView.backgroundColor = .green
return footerView
}

How to save an Array in UserDefault

I'm using a UITabBarController to create a contact list, but when I'm trying to save the array to load the data when I restart the app is giving me problems where the data isn't displayed. I'm using UserDefaults to save the data and the restore when the app is restarted.
In this code I sent data from a textfield to the array named list.
import UIKit
class NewContactoViewController: UIViewController {
#IBOutlet weak var input: UITextField!
#IBAction func add(_ sender: Any) {
if (input.text != "") {
list.append(input.text!)
UserDefaults.standard.set(list, forKey: "SavedValue")
input.text = ""
}
}
}
In this code I'm printing the data in a table, and trying to save it with user defaults.
import UIKit
var list = [String]()
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let x = UserDefaults.standard.object(forKey: "SavedValue") as? String {
return (x.count)
}
return (0)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
if let x = UserDefaults.standard.dictionary(forKey: "SavedValue") as? String {
cell.textLabel?.text = [x[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 viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
}
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
You are saving an array of strings but you are reading a single string (or even a dictionary) which obviously cannot work. There is a dedicated method stringArray(forKey to read a string array.
Apart from the issue never read from UserDefaults to populate the data source in the table view data source and delegate methods, do it in viewDidLoad or viewWillAppear for example
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let savedArray = UserDefaults.standard.stringArray(forKey: "SavedValue") {
list = savedArray
}
myTableView.reloadData()
}
Put the data source array in the view controller. A global variable as data source is very bad programming habit.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var list = [String]()
...
In numberOfRowsInSection return the number of items in list and return is not a function, there are no parentheses
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
Same in cellForRow. Get the item from list and use reusable cells and again, return is not a function.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = list[indexPath.row]
return cell
}
Note :
Consider that UserDefaults is the wrong place to share data between view controllers. Use segues, callbacks or protocol / delegate.

How to get Label text of UITableView Cell using Swift?

I want to make simple UITabelView. I registered it's cell with my custom class which named as backgroundviewcell. I want to get Label of the selected cell. I tried many times but the output is coming nil value. I have also tried solution from stack overflow but it does not work for me. This is my cellForRowAt indexPath code :
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> backgroundViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "soundElementalcell") as! backgroundViewCell
let imageNames = sections[indexPath.section].images[indexPath.row]
cell.Labeltittle.text = sections[indexPath.section].items[indexPath.row]
cell.Labeltittle.textColor = UIColor(hex : 0x90BA27)
cell.LabelDetail.text = sections[indexPath.section].detail[indexPath.row]
//cell.detailTextLabel?.textColor = UIColor.red
//cell.isHighlighted = false
cell.backgroundColor = UIColor.clear
cell.iconview.image = UIImage(named: imageNames)
return cell
}
This is my didSelectRowAt indexPath code :
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)!
celltext = currentCell.textLabel!.text
performSegue(withIdentifier: "showPlayer", sender: self)
}
and my Segue Method is :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPlayer" {
let playerVC = segue.destination as! PlayerViewController
playerVC.trackName = (celltext)! as String
}
}
This is working for me:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! YourCellType
let labelContent = cell.labelToAccess.text
}
First line creates an instance of your cell type (or standard cell, not my case).
The second saves the content of your label (labelToAccess in this case) in a constant.
Instead of accessing the text from the cell's label you need to access the array that you have used to fill the tableView's cell data.
So you need to use UITableViewDelegate method didSelectRowAtindexPath and access the array that you are using with your tableView methods.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
//Access the array that you have used to fill the tableViewCell
print(yourArray[indexPath.row])
}
Note: Once confirm that TableView delegate is properly connected with your ViewController so that it will call didSelectRowAt method when you select cell.
Edit: If you want to pass the data then try like this way.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showPlayer", sender: indexPath) //Pass indexPath as sender instead of self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPlayer" {
let playerVC = segue.destination as! PlayerViewController
let indexPath = sender as! IndexPath
playerVC.trackName = sections[indexPath.section].items[indexPath.row]
}
}
Try this 'Swift 4' -
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCellTxt = yourTableView.cellForRow(at: indexPath)! as? YourCustomTableViewCell
print(currentCellTxt?.lblYourName?.text) // 'lblYourName' that you defined in your 'YourCustomTableViewCell'
}
You didn't set any text for textLabel. Hence it returns nil value.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> backgroundViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "soundElementalcell") as! backgroundViewCell
let imageNames = sections[indexPath.section].images[indexPath.row]
cell.Labeltittle.text = sections[indexPath.section].items[indexPath.row]
cell.Labeltittle.textColor = UIColor(hex : 0x90BA27)
cell.LabelDetail.text = sections[indexPath.section].detail[indexPath.row]
//cell.detailTextLabel?.textColor = UIColor.red
//cell.isHighlighted = false
cell.backgroundColor = UIColor.clear
cell.iconview.image = UIImage(named: imageNames)
return cell
}
In didSelectRow func try to get value from Labeltittle.text not from textLabel.text
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)!
celltext = currentCell.Labeltittle.text
performSegue(withIdentifier: "showPlayer", sender: self)
}

'UITableView' failed to obtain a cell from its dataSource

I updated Xcode and since then I'v problems with my dataBase.
code:
override func numberOfSections(in tableView: UITableView) -> Int {
// return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
// return the number of rows
return leadItems.count
}
func cellForRow(at indexPath: IndexPath) -> UITableViewCell?
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! LeadsTableViewCell
// Configure the cell...
cell.user_name_label.text = leadItems[indexPath.row].user_name
cell.school_name_label.text = leadItems[indexPath.row].school_name
return cell
}
The error that I get is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView (; layer = ; contentOffset: {0, -64}; contentSize: {375, 65802}>) failed to obtain a cell from its dataSource ()
Your method name for cellForRow is not correct.
Replace this line
func cellForRow(at indexPath: IndexPath) -> UITableViewCell?
{
with
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
Add this to your viewDidLoad method:
tableView.register(LeadsTableViewCell.self, forCellReuseIdentifier: "Cell")
This is assuming you don't have a prototype cell in the tableView in your storyboard. If you do have a prototype cell, ensure the Reuse Identifier is "Cell" and the custom class of the cell is LeadsTableViewCell; then there'll be no need to register the cell.
UPDATE
I just noticed your method name is wrong, it should be:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! LeadsTableViewCell
// Configure the cell...
cell.user_name_label.text = leadItems[indexPath.row].user_name
cell.school_name_label.text = leadItems[indexPath.row].school_name
return cell
}
In order to get a cell for your table view you must implement the data source method that is designed to provide the table view with a cell for a given row at an index path. This method is:
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
Because this method is a required method you can not leave it out or you will get an error. Configure your cell in this method.

Resources