Fatal error: Array index out of range in swift2 version - ios

Please help with the error I have
on this line:
cell.name.text = names[indexPath.row] //error array index out of range
This is my declaration:
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var names = ["Anna","aanal"]
var petnames = ["Anu","aalu"]
#IBOutlet var tableView: UITableView!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
cell.name.text = names[indexPath.row] //error array index out of range
cell.petname.text = petnames[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Your array only has 2 elements while your numberOfRowsInSection method returns 3. Either change it to 2 or add 3 elements in your array
The ideal way is to change
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
But usually dont work with 2 arrays and create a Entity object having name and petname as elements and then have an array of your entity class to populate tableview

Try this :
I think your are passed petnames in numberOfRowsInSection and names and petnames count are also different so this problem occurs .
Solution
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if names.count == petnames.count{
return petnames.count
}
return 0;
}
OR
You have passed more numberOfRowsInSection then array.count
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2 or names.count
}

There are 2 values in your arrays names and petnames. However, there are 3 rows in your Table View. So, if the 3rd row of the table view is selected, it will be out of range of the array as there is no 3rd element in the arrays.
The solution is to make your names array and petnames array contain 3 elements:
var names = ["Anna","aanal", "boo"]
var petnames = ["Anu","aalu", "bo"]
or to return 2 rows in your table view:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
The best way is to return the count of the arrays (if the count of both arrays are different, return nothing):
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if names.count == petnames.count {
return names.count
}
return 0
}

Just pass the count of array
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourArray.count
}

var names = ["Anna","aanal"] // you have 2 elemnts
var petnames = ["Anu","aalu"]
#IBOutlet var tableView: UITableView!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//return 3 // you have returned 3 but it should be 2
// it should be
return 2
//or
// return names.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
cell.name.text = names[indexPath.row] //you got error because you are trying to get 3 element from array where as there are only 2 elements
cell.petname.text = petnames[indexPath.row]
return cell
}
It is better if you use your arrayname.count

Related

Need to get a logic for implementing the tableview sections according to array count

My scenario is that I have three different types of arrays that might or might not contain values. I have 3 sections with section headers for my tableview. I am having trouble finding a solution that would be to dynamically set the sections i.e, if one of my arrays doesn't have a value then I don't want to show the section. If 3 arrays have value then show the 3 sections or if any one of the arrays doesn't have value then I don't want to show that section.
Your numberOfSections will be the number of arrays. And the numberOfRowsInSection will be the count of each arrays for that section in your tableViewDataSource.
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return array1.count
} else if section == 1 {
return array2.count
} else {
return array3.count
}
}
If there are no items in an array, then the rows will be zero for that section.
You can do something like this
// Create enum for simplifying the implementation.
enum SectionType{
case type1
case type2
case type3
}
class TestVC: UIViewController {
// create to show sections only when data is available
var sections: [SectionType] = []
// create you array types
var array1 = [String]()
var array2 = [String]()
var array3 = [String]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
// Add enums to section only when array has some value
// You can do this when you get API data
if array1.count > 0{
sections.append(.type1)
}
if array2.count > 0{
sections.append(.type2)
}
if array3.count > 0{
sections.append(.type3)
}
}
}
extension TestVC: UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
// only those sections with data wil be visible
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch sections[section]{
case .type1:
return array1.count
case .type2:
return array2.count
case .type3:
return array3.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section]{
// return different type of cells if required accourding to the data in array
case .type1, .type2, .type3:
return UITableViewCell()
}
}
}
If the arrays can change dynamically (after the view is loaded), you can implement number of sections like that:
func numberOfSections(in tableView: UITableView) -> Int {
var numberOfSections = 0
if array1.count > 0 { numberOfSections++ }
if array2.count > 0 { numberOfSections++ }
if array3.count > 0 { numberOfSections++ }
return numberOfSections
}
Your data source should look like this - sectionModels = [[cellModels]]
The outer array represents number of sections and inner array represents number of cells in that section.
func numberOfSections(in tableView: UITableView) -> Int {
return sectionModels.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sectionModels[section].count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellModel = sectionModels[indexPath.section][indexPath.row]
// Configure UITableViewCell with cellModel
}
}

UITableView not showing all sections

I have a problem that I want to show a tableview, but separated in sections by the "status" of each item. I know how to do it with a simple string array, but I can't get to make this work with a class (Aluno) array, here's my code so far:
import UIKit
class DeliveriesTVC: UITableViewController {
let sections = ["Delivered", "Not Delivered"]
var studentList: [Array<Student>] = [[], []]
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...5{
studentList[0].append(Student(alunoNome: "Aluno \(i)", alunoImg: "dani_test", alunoStatus: "Delivered"))
}
for i in 6...10{
studentList[1].append(Student(alunoNome: "Aluno \(i)", alunoImg: "dani_test", alunoStatus: "Not Delivered"))
}
self.title = "Deliveries"
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cellEntrega = tableView.dequeueReusableCell(withIdentifier: "EntregaCell", for: indexPath) as? EntregaCell {
let entregaCell = studentList[indexPath.section][indexPath.row]
// here i call a function in my TableViewCell class that update the cell itself
cellEntrega.updateAlunoUI(Aluno: entregaCell)
return cellEntrega
} else {
return UITableViewCell()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listaAlunos[section].count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
}
In the output, i just get the "first section" showing, and without a name, even with me setting the name of each section and the number of sections. I've looked everywhere but i couldn't find a solution.
Your numberOfSections and titleForHeader methods are wrong, it should be
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
Additionally, you should return self.sections.count instead of return 2 in numberOfSections being hardcoded as in case you add another object to the array, you will have to change the 2 to whatever elements the array has now.
For your numberOfSectionInTableView function, shouldn't it be override func numberOfSectionsInTableView(in tableView: UITableView) -> Int {
return 2
} with a in in front of the tableView: UITableView?
I don't see where you connect your UITableView's delegate and datasource.
Here is a tutorial to show your about using UITableView.
Take a look at "Basic Table View" - "Step 3: Set the datasource and delegate"

index out of range in when we using multi-dimentional array

class ViewController:UIViewController,UITableViewDelegate,UITableViewDataSource {
var sections = ["A","B","C","D"]
var items = [["General","Privacy",""],["icloud","Maps","News"],["safari","Photos and Camera","Game Center"],["Twitter","Facebook","Flicker"]]
var secItems = ["iclouds","Maps","Safari"]
var thirItems = ["Twitter","General","Privacy","icloud"]
var imgItems = ["settings_ios7_ios_7.png","privacy.jpeg","icloud.png","Google-Maps-4.0-for-iOS-app-icon-small.png","news.jpeg","safari_ios7_ios_7 (1).png","photos.jpeg","game_center_ios7_ios_7.png","twitter.jpeg","facebook.jpeg","flicker.png"]
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Settings"
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "ABC"
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
cell.lblName.text = items[indexPath.section][indexPath.row]
cell.imgName.image = UIImage(named: imgItems[indexPath.row])
cell.arrow.image = UIImage(named: "arrowIcon.png")
return cell
}
}
Getting error index out of range in here:
cell.lblName.text = items[indexPath.section][indexPath.row]
(when we take 4 by 4 multi-dimensional array it works fine, but we take different array it's getting index out of range.
In numberOfRowsInSection you are returning items.count which will be count of total array in your Item array... which is actually number of sections.count which is wrong. It should be items[section].count
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items[section].count
}

iOS Swift - Show Contacts in TableView using UILocalizedIndexedCollation

// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
print("titleForHeaderInSection: \(collation.sectionTitles[section])")
return collation.sectionTitles[section]
}
override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String] {
print("sectionIndexTitlesForTableView: \(collation.sectionIndexTitles)")
return collation.sectionIndexTitles
}
override func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
print("sectionForSectionIndexTitle: \(sections.count)")
return collation.sectionForSectionIndexTitleAtIndex(index)
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.results.count > 0) ? self.results.count : 0
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
let numberOfSections = UILocalizedIndexedCollation.currentCollation().sectionTitles.count
print("numberOfSections: \(numberOfSections)")
return numberOfSections
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("contactscell") as UITableViewCell!
let label = cell.viewWithTag(1) as? UILabel
label?.text = self.results[indexPath.row].givenName
return cell
}
It Display all contacts in every sections. I want to show contacts in sorted order with alphabetical index a to z
You're returning self.results.count in numberOfRowsInSection regardless of which section you are in. You have to return the number of rows for each letter. Consider using a dictionary with letters as the keys and contacts as the values.

How can I group TableView items from a dictionary in swift?

Lets consider this example:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var names = ["Vegetables": ["Tomato", "Potato", "Lettuce"], "Fruits": ["Apple", "Banana"]]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier:"test")
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return ???
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int{
return names.count
}
func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]!{
return ???
}
func tableView(tableView: UITableView,
titleForHeaderInSection section: Int) -> String?{
return ????
}
}
let's assume that we need that the keys (fruits and vegetables) of the dictionary are the number of sections, plus they will be the titles of the sections. The items of the keys (eg apples and banana) will be the rows of each section. How can I implement this in my code? I know it might be easy but I couldn't figure it out my self.
You can use struct for that and here is example:
import UIKit
class TableViewController: UITableViewController {
var names = ["Vegetables": ["Tomato", "Potato", "Lettuce"], "Fruits": ["Apple", "Banana"]]
struct Objects {
var sectionName : String!
var sectionObjects : [String]!
}
var objectArray = [Objects]()
override func viewDidLoad() {
super.viewDidLoad()
for (key, value) in names {
println("\(key) -> \(value)")
objectArray.append(Objects(sectionName: key, sectionObjects: value))
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return objectArray.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectArray[section].sectionObjects.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel?.text = objectArray[indexPath.section].sectionObjects[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return objectArray[section].sectionName
}
}
Swift 2
you dictionary example
var dic:Dictionary<String,String> = ["key":"value","key1":"value2"]
Your table
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
var key = Array(self.dic.keys)[indexPath.row]
var value = Array(self.dic.values)[indexPath.row]
cell.text = key + value
}
If you want it sorted use the global sorted function to sort the dictionary.
import UIKit
class TableViewController: UITableViewController {
var names = ["Vegetables": ["Tomato", "Potato", "Lettuce"], "Fruits": ["Apple", "Banana"]]
var namesSorted = [String, Array<String>]()
override func viewDidLoad() {
super.viewDidLoad()
// Sort names
namesSorted = sorted(names) { $0.0 < $1.0} // namesSorted = ["Fruits": ["Apple", "Banana"], "Vegetables": ["Tomato", "Potato", "Lettuce"]]
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return namesSorted.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return namesSorted[section].1.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel?.text = namesSorted[indexPath.section].1[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return namesSorted[section].0
}
}
All collection types must be Array
var names = [["Tomato", "Potato", "Lettuce"], ["Apple", "Banana"]]
var sectionNames = ["Vegetables", "Fruits"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return names[section].count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int{
return names.count
}
func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]!{
return sectionNames
}
func tableView(tableView: UITableView,
titleForHeaderInSection section: Int) -> String?{
return sectionNames[section]
}
From Apple Documentation :
var keys: LazyForwardCollection<MapCollectionView<Dictionary<Key, Value>, Key>> { get }
Description: A collection containing just the keys of self. Keys appear in the same order as they occur as the .0 member of key-value pairs in self. Each key in the result has a unique value.
names.keys.array returns an Array of the keys.
SO:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return names.keys.array[section].count
}
func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]!{
return names.keys.array
}
func tableView(tableView: UITableView,
titleForHeaderInSection section: Int) -> String?{
return names.keys.array[section]
}
This will work on Any Dictionary with any amount of data(even if it is unknown to the programmer
An easier way to solve this problem is to copy your dictionary into a temporary variable. Use removeFirst to extract the values from the array inside the dictionary.
var itemList=["Grocery":["soap","flour","carrots"],"Vehicles":["oil change","gas","tire rotation"],"Household":["Cable","Tv","cellphone"]]
var itemListTmp :[String:[String]] = [:]
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text=itemListTmp[keysItem[indexPath.section]]?.removeFirst()
//cell.textLabel?.text=itemList[indexPath.section].items[indexPath.row]
return cell
}
Another way of solving this problem is to extract keys and values in separate arrays:
var task=[String](itemList.keys)
var tobeDone=[[String]](itemList.values)
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return task[section]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text=tobeDone[indexPath.section][indexPath.row]
return cell
}
Similar to https://stackoverflow.com/a/31136537/11098567 answer I would use classes instead of structs, so that you can manipulate or add to your values after it has been placed into the array.
#objc func addToInitialClassInstance() {
let classInstance = Class(property1: String, property2: [CLass2.init(property1: String, property2: String)])
let isAvailable = initialClassInstance.contains { (classInArray) -> Bool in
if classInArray.property == classInstance.property {
classInArray.property2.append(classInstance.property2[0])
return true
}
return false
}
if !isAvailable {
initialClassInstance.append(classInstance)
}
tableView.reloadData()
}

Resources