Swift - Array not being populated with values - ios

I am trying to build a list within a table view controller and I have the proper setup, but for some reason my simulator crashes at the line where I set my array. It is not an error, but a Thread issue. I'm still learning the XCode warning system so I'm not sure what that means, but I noticed that in the Thread notifications that cityArray = ([String]) 0 values. Can anyone help?
import UIKit
class ListTableViewController: UITableViewController {
var cityArray: [String] = ["Portland","San Francisco","Cupertino"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cityArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellIdentifier", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = self.cityArray[indexPath.row]
return cell
}
}
UPDATE:
Images of the Thread message:

try this
let cityArray: [NSArray] = ["Portland","San Francisco","Cupertino"] as NSArray

Can you also provide the error you're receiving? Is it an issue with the array being mutated while being enumerated or something?
Also,
var cityArray: [String] = ["Portland","San Francisco","Cupertino"]
can change to
var cityArray = ["Portland", "San Francisco", "Cupertino"]
Since all your objects are of the same type, the Swift's type inference takes care of this for you.

Related

TableView NSException

I am new to Swift , I am parsing my JSON by using ObjectMapper but I want data to be displayed in TableView. But I have a problem:
libc++abi.dylib: terminating with uncaught exception of type NSException
I get it after the method numberOfRowsInSection. My array is not nil, array has a 2193 elements
I do not understand why it happened
It my code for parsing JSON :
let timeStamp = NSNumber(value: Date().timeIntervalSinceNow)
var programs = [PrograToDayModel]()
override func viewDidLoad() {
super.viewDidLoad()
super.viewDidLoad()
let timeStamp = NSNumber(value: Date().timeIntervalSinceNow)
self.downloadPrograms(for: timeStamp)
}
func downloadPrograms(for timestamp: NSNumber) {
Alamofire.request("http://52.50.138.211:8080/ChanelAPI/programs/\(timestamp)").responseArray { (response: DataResponse<[PrograToDayModel]>) in
let programlArray = response.result.value
if let programlArray = programlArray {
for program in programlArray {
self.programs.append(program)
print(program.title as Any)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
it good i print element in console :
my code for table:
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print(self.programs.count as Any)
return self.programs.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProgramTableViewCell", for: indexPath) as! ProgramTableViewCell
cell.title.text = self.programs[indexPath.row].title
return cell
}
}
All identifiers in place
I using tab bar, tableView, tableViewCell
How can I solve this problem?
To identify the issue, you can just try this -
it might be a reason for that issue
So go to Main.storyboard, and right-click on View Controller at the top of the phone outline and remove any outlets with yellow flags (if any).
I was getting a similar non descriptive error when trying to initialize a uitableviewcontroller when trying to add a section/number of rows. Did you register a tableview cell class? I see that you have a custom tableview cell created, so if that isn't registered with your tableview that might be causing this error.
tableView.register("ProgramTableViewCell".self, forCellReuseIdentifier: "ProgramTableViewCell")

Cannot subscript a value of type 'inout [MenuItem]' (aka 'inout Array<MenuItem>')

I have this weird error message in my TableViewController class
class MenuTableViewController: UITableViewController {
fileprivate var menuItems = [MenuItem]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "MenuItemTableViewCell", bundle: nil), forCellReuseIdentifier: CELL_MENU_ITEM)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CELL_MENU_ITEM, for: indexPath) as! MenuItemTableViewCell
// this line throws the error message
if let menuItem = self.menuItems[indexPath.row].getTitle() as [MenuItem] {
cell.itemTitleLabel.text = menuItem
}
return cell
}
func setMenuItems(menuItems: [MenuItem]) {
self.menuItems = menuItems
}
}
I totally don't know what that error means. There are others facing this problem with type inout but they are doing errors with '=' instead of '==' and things like that. By the way the value of menuItems get set in another class in a completion function. But if I remove it from there I still got this error.
Since menuItems is declared as a concrete non-optional type there is no type casting nor optional binding needed.
let menuItem = self.menuItems[indexPath.row]
cell.itemTitleLabel.text = menuItem.getTitle()
The error message might be misleading. You are trying to cast (presumed) String to [MenuItem]
Maybe you want to check MenuItem? like that:
if let menuItem = self.menuItems[indexPath.row] as MenuItem {
cell.itemTitleLabel.text = menuItem.getTitle()
}
And you declare your collection with [MenuItem] type, so subscription will return non-optional value, you can remove iflet check and use:
cell.itemTitleLabel.text = self.menuItems[indexPath.row].getTitle()
I have missed to import uikit in my custom struct and get a similar error. hope it helps somebody reading here.

UITableView populated but not displaying data or rows

I know this particular question has been asked and answered previously in SO but cross checked those answers and still not able to fix this issue. Can be a silly mistake but unable to nail it.
Cross checked :
Cell Identifierid
datasource and delegate added through Interface builder
Code :
var sensorFields = [Sensor]()
override func viewDidLoad() {
super.viewDidLoad()
readParseDataFromCSV(file: "csvFile")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.sensorFields.count)// has count 120
return self.sensorFields.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SensorTableCell") as! SensorTableViewCell
cell.lblSensorName.text = sensorFields[indexPath.row].labelName
cell.lblSensorValue.text = sensorFields[indexPath.row].labelValue
print(sensorFields[indexPath.row].labelName) // doesn't seem to enter to this code block
print(sensorFields[indexPath.row].labelValue)
return cell
}
func readParseDataFromCSV(file:String){
let filepath = Bundle.main.path(forResource: file, ofType: "csv")!
do {
let csv = try CSV(contentsOfURL: filepath)
let rows = csv.rows
for row in rows{
let sensorlblName = row["data column 1"]
let sensorlblValue = row["data column 2"]
let sensor = Sensor(labelName: sensorlblName!, labelValue: sensorlblValue!)
sensorFields.append(sensor)
}
} catch {
print(error.localizedDescription)
}
}
class SensorTableViewCell: UITableViewCell {
#IBOutlet weak var lblSensorName: UILabel!
#IBOutlet weak var lblSensorValue: 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
}
}
Is there anything missed out , using other tableviews in the app already which is working perfectly apart from this one.
Using Xcode 8.1 and Swift 3.0
Edit :
Added reloadData()
Seems like
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
is not firing too.
Please help.
Thanks in advance.
Just appending data to your sensorFields array has no impact on the cells displayed in your tableView.
You need to call reloadData on the tableView so it knows the data did change and repopulates the cells with the new data.
for row in rows {
let sensorlblName = row["data column 1"]
let sensorlblValue = row["data column 2"]
let sensor = Sensor(labelName: sensorlblName!, labelValue: sensorlblValue!)
sensorFields.append(sensor)
}
tableView.reloadData()
I would also prefer
// newer dequeue method guarantees a cell is returned and resized properly, assuming identifier is registered
// Swift 2
func dequeueReusableCellWithIdentifier(identifier: String, forIndexPath indexPath: NSIndexPath) -> UITableViewCell
// Swift 3
func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell
to
// Used by the delegate to acquire an already allocated cell, in lieu of allocating a new one.
// Swift 2
func dequeueReusableCellWithIdentifier(identifier: String) -> UITableViewCell?
// Swift 3
func dequeueReusableCell(withIdentifier identifier: String) -> UITableViewCell?
because dequeueReusableCellWithIdentifier(identifier: String) does not guarantee returning a UITableViewCell when the cell with identifier was not yet allocated. ("[...] acquire an already allocated cell, in lieu of allocating a new one.")
And make sure your IBOutlets are connected! (lblSensorName and lblSensorValue)
check this
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("SensorTableCell") as UITableViewCell!
if !cell {
cell = UITableViewCell(style:.Default, reuseIdentifier: "SensorTableCell")
}
}

SWIFT - UITableViewCell updating based on selection

I have a TableViewController (lets call TVC1) with a row that says "OD" (which stands for Outer Diameter).
Upon selecting this row, a bunch of rows in a new TableViewController (lets call TVC2) containing the various OD (casingOD in my code) shows. What I want to happen is when the user selects the OD it will segue back to the main TableViewController with the string that corresponds to the user selection. My code for this currently fails...Could anyone help point me in the right direction? If you require TVC1 code i'll happily post it, i'm just trying to save any unneccessary code reading for you folks :)
My TVC2 code is as follows:
import UIKit
class CasingSelectionTableViewController: UITableViewController {
var selectedData: Data?
let casingOD = ["114.3", "127.0", "139.7", "168.3" , "177.8", "193.7", "219.1", "244.5", "247.6", "273.1", "298.4", "298.4", "339.7", "406.4", "473.0", "508"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
switch selectedData! {
case .OuterDiameter:
print(casingOD)
case .Weight:
print(casingWeight114) // I deleted the casingWeight114 line of code as its not required for this question
case .InnerDiameter:
print(id114) // I deleted the id114 line as its not required for this question
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return casingOD.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var casingSpec: UITableViewCell!
if selectedData == Data.OuterDiameter {
casingSpec = tableView.dequeueReusableCellWithIdentifier("selectedCasingSpec", forIndexPath: indexPath)
let casingODSpec = casingOD[indexPath.row]
casingSpec.textLabel?.text = casingODSpec
return casingSpec
} else {
return casingSpec
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selection: UITableViewCell!
selection.textLabel?.text = indexPath.row as! String
}
What I want to happen is when the user selects the OD it will segue back to the main TableViewController with the string that corresponds to the user selection.
First of all you'll need to implement a way for TVC2 to notify TVC1 that a value has been selected.
A common way to do such thing is by using delegation. You can define a delegate protocol like this:
protocol TVC2Delegate {
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String)
}
Then add a var delegate: TVC2Delegate? property to TVC2.
You'll then make TVC1 comform to TVC2Delegate by implementing that method in TVC1.
When presenting TVC2 from TVC1 remember to set it as the delegate for TVC2.
// In TVC1
tvc2.delegate = self
To connect TVC1 and TVC2 you could add a bit o logic to your tableView(tableView:,didSelectRowAtIndexPath:) method call the delegate with the selected value
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let stringValue = indexPath.row as! String
// Do anything you need to do related to TVC2 here.
// Then finally
delegate?.tvc2(self, didSelectOuterDiameter: stringValue)
}
Finally, in TVC1's implementation of the delegate method you can take care of dismissing TVC2 if needed.
Update:
This is how the final implementation of these bits might look like:
// In TVC1
class TVC1: UITableViewController, TVC2Delegate {
// ...
// Implement the method(s) of TVC2Delegate
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String) {
// Do whatever you need to do with the outerDiameter parameter
}
}
// In TVC2
protocol TVC2Delegate {
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String)
}
class CasingSelectionTableViewController: UITableViewController {
var delegate: TVC2Delegate?
// ...
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let stringValue = casingOD[indexPath.row]
// Do anything you need to do related to TVC2 here.
// Then finally
delegate?.tvc2(self, didSelectOuterDiameter: stringValue)
}
}
Use the delegate approach as suggested in the answer by #Mokagio. And in case you're having issue in getting the string, here is the answer
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! UITableViewCell
let stringValue = cell.textLabel.text //You can get this from your datasource as well)
//call the delegate
}

Swift - iOS - Multiple table view controllers sharing a single data source.

I'd like to get started using swift to make a small list based application. I was planning on using two table view controllers to display the two lists, and was wondering if it were possible to have them share a common data source.
Essentially the data would just be an item name, and two integers representing the amount of the item owned vs needed. When one number increases, the other decreases, and vice versa.
I figured this might be easiest to do using a single data source utilized by both table view controllers.
I did some googling on shared data sources and didn't find anything too useful to help me implement this. If there are any good references for me to look at please point me in their direction!
You can create one data source class and use it in both view controllers:
class Item {
}
class ItemsDataSource: NSObject, UITableViewDataSource {
var items: [Item] = []
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
//setup cell
// ...
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
}
class FirstViewController : UITableViewController {
var dataSource = ItemsDataSource()
override func viewDidLoad() {
self.tableView.dataSource = dataSource
self.tableView.reloadData()
}
}
class SecondViewController : UITableViewController {
var dataSource = ItemsDataSource()
override func viewDidLoad() {
self.tableView.dataSource = dataSource
self.tableView.reloadData()
}
}
use singleton design pattern, it means both tables will get data source from the same instance
class sharedDataSource : NSObject,UITableViewDataSource{
static var sharedInstance = sharedDataSource();
override init(){
super.init()
}
//handle here data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
}
}
var tableOne = UITableView();
var tableTwo = UITableView();
tableOne.dataSource = sharedDataSource.sharedInstance;
tableTwo.dataSource = sharedDataSource.sharedInstance;
The first argument to the delegate method is:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
At that point, your one Datasource delegate can decide which table view is wanting a cell, for example, and return results accordingly.

Resources