I declared a class with all the variables in it.
class Xyz
{
var a: String?
var b: String?
}
In other viewController, I am declaring an array of that class.
var arr = [Xyz]()
var arr2 = ["title1","title2"]
After Json Parsing, I am appending the values in this array.
var temp = Xyz()
var dict = item as! NSDictionary
temp.a = (dict.value(forKey: "a") as? String) ?? ""
temp.b = (dict.value(forKey: "b") as? String) ?? ""
self.arr.append(temp)
How should I display this array in a cell?
cell.textLabel?.text = arr2[indexPath.row]
//The above array shows the title of the row
cell.detailTextLabel?.text = String(describing: arr[indexPath.row])
//indexPath doesn't work here (error: indexPath out of range)
//The reason is the 'arr' array has class in it
The above statement gives error as the array has class in it and not the values.
cell.detailTextLabel?.text = String(describing: arr[0].a)
cell.detailTextLabel?.text = String(describing: arr[0].b)
is the only way I can access my values.
Due to which I am unable to display this array in my tableView.
How to display the content of the array on a tableView cell(each on separate cell)?
There are many mistakes / bad programming habits in the code.
First of all name the class starting with a capital letter and declare the properties non-optional because they are going to contain non-optional values. (Declaring optionals as an alibi not to write an initializer is one of the bad habits.)
Include the title of the row from arr2 as title property in the class, that avoids any out of range exception.
class Xyz
{
var a : String
var b : String
var title : String
init(a: String, b: String, title: String) {
self.a = a
self.b = b
self.title = title
}
}
Declare the data source array
var arr = [Xyz]() // `var arr: [xyz]()` does not compile
Populate the data source array
let dict = item as! [String:Any] // Swift Dictionary !!
let temp = Xyz(a: dict["a"] as? String) ?? "", b: dict["b"] as? String) ?? "", title: "title1")
self.arr.append(temp) // `self.arr.append(value)` does not compile
In cellForRow get the Xyz instance from the array and use the properties
let item = arr[indexPath.row]
cell.textLabel?.text = item.title
cell.detailTextLabel?.text = item.a + " " + item.b
Since all properties are strings anyway, all String(describing initializers (create String from String) are nonsensical.
It seems like you want to display your class properties
Replace the line
cell.detailTextLabel?.text = String(describing: arr[indexPath.row])
With
cell.detailTextLabel?.text = "\(arr[indexPath.row].a) \(arr[indexPath.row].b)"
I went through all the below answers. But none of them produced the solution. The problem in all solution is the array is printed on same cell, leaving the other cell empty(including the answer provided by Vadian - it will give error because it is printing all the value in same row.). While printing an array on a cell, you have to go in a loop, But none of the answers provides that. This will give error back Index out of range .The best solution I came across is to use switch or enum. Due to switch or enum you can put a condition for every row and according to that you can print the item from the array. Here I put the simple array item "title" as case and according to that printed the array of class.
Solution:- The following code helped me to achieve what I asked.
Note:- enum is more preferred than switch. I used switch because easy to understand and got my work done.
let a = arr2[indexPath.row]
let item = arr[0]
switch a
{
case "title1":
cell.detailTextLabel?.text = item.a
return cell
case "title2" :
cell.detailTextLabel?.text = item.b
return cell
default:
break
}
Related
I have an array of dictionaries, [[String:AnyObject]], which is reduce+sorted as below successfully.
var arrUserList = [(key: String, value: [[String : Any]])]()
let result = self.arrJsonDict.reduce(into: [String: [[String:Any]]]()) { result, element in
let strName: String = (element as! NSDictionary).value(forKey: "name") as! String
if let firstLetter = strName.first {
let initial = String(describing: firstLetter).uppercased()
result[initial, default: [[String:Any]]() ].append(element as! [String : Any])
}}.sorted { return $0.key < $1.key }
self.arrUserList = result
Now I wanted to assign keys to table sections and values as table cell text from the array.
This is very cumbersome code.
You are highly encouraged to use a struct rather than a dictionary at least with a member name
struct Person {
let name : String
}
Declare and rename arrJsonDic (more descriptively) as
var people : [Person]()
and arrUserList as
var users = [String: [Person]]()
For the sections declare another array
var letters = [String]()
Group the array and populate letters simply with
users = Dictionary(grouping: people, by: { String($0.name.first!) })
letters = users.keys.sorted()
In the table view in numberOfSections return
return letters.count
and in numberOfRows return
let letter = letters[section]
return users[letter]!.count
In cellForRowAt assign a name to a label with
let letter = letters[indexPath.section]
let user = users[letter]![indexPath.row]
cell.nameLabel.text = user.name
------------------------------
To make it still swiftier declare a second struct Section
struct Section {
let index : String
let people : [Person]
}
delete
var letters = [String]()
and declare users
var users = [Section]()
The grouping is slightly different
let grouped = Dictionary(grouping: people, by: { String($0.name.first!) })
users = grouped.map({ Section(index: $0.0, people: $0.1) }).sorted{$0.index < $1.index}
The code in the three table view delegate methods are
return users.count
-
return users[section].people.count
-
let user = users[indexPath.section].people[indexPath.row]
cell.nameLabel.text = user.name
Here I am having Json data which I am retrieving from model class in which I need to find if the key value pair title having word starting with Discount for example if I find key value pair starting with Discount I need to set the particular table view cell label as Discount how to find it from key value pairs can anyone help me how to implement this ?
Here is my model class data
struct TotalPriceSegments {
let code : String?
let title : String?
let value : Double?
init(json: [String:Any]) {
self.code = json["code"] as? String
self.title = json["title"] as? String
self.value = json["value"] as? Double
}
}
Here is table view code
let cell = tableView.dequeueReusableCell(withIdentifier: "checkout", for: indexPath) as! CheckoutTableViewCell
let array = totalPriceModel?.totalSegments[indexPath.row]
cell.titleLabel.text = array?.title
let total = doubleToStringDecimalPlacesWithDouble(number: Double((array?.value)!), numberOfDecimalPlaces: 2)
cell.valueLabel.text = (String(describing:("$ \(total)")))
return cell
In cellForRow method add such check:
if array?.title.hasPrefix("Discount") {
cell.titleLabel.text = "Discount"
}
Hello i have variables but gives all of them Optional(). How can i resolve them my codes under below.
Json append codes for koltuklar koltuklaridler array under below you can see
for name in json as! [AnyObject] {
let SeatName = name["SeatName"]
let SeatDesignId = name["SeatDesignId"]
self.koltuklar.append("\(SeatName)*\(SeatDesignId)*")
if let blogs = json["SeatDetail"] as? [[String: AnyObject]] {
for blog in blogs {
let TicketTypeId = blog["TicketTypeId"]
let TicketTypeName = blog["TicketTypeName"]
let Amount = blog["Amount"]
self.koltuklaridler.append("\(SeatDesignId)*\(TicketTypeId)*\(TicketTypeName)*\(Amount)*")
}
}
Under below you can see tableview inside codes ( That codes doing open koltuklar index path item after search id inside koltuklaridler and when found take some varibles from it )
var koltuklar = [""]
var koltuklaridler = [""]
if let myStrings:String! = koltuklar[indexPath.row]{
print("\(myStrings!)")
let myStringArrf = myStrings.componentsSeparatedByString("*")
print("\(myStringArrf)")
if let koltukisims:String! = String(myStringArrf[0]) {
cell.koltukName.text = koltukisims
}
print(" STR - \(myStringArrf[1] as String!)")
if let index = koltuklaridler.indexOf(myStringArrf[1] as String!) {
let myStringdetaysecilen = koltuklaridler[index]
print("myStringdetaysecilen \(myStringdetaysecilen)")
}
Also my json file
[
{
"SeatDesignId": 16484,
"SeatName": "A6",
"SaloonId": 148,
"SeatDetail": [
{
"TicketTypeId": 1,
"TicketTypeName": "Okay",
"Amount": 13
}
]
},
Output
Optional("A17")*Optional(16254)*
["Optional(\"A17\")", "Optional(16254)", ""]
STR - Optional(16254)
All variables output Optional i try everything but doesn't fix.
As mentioned in my comments, whenever you use String Interpolation "\(...)" make sure that all optional strings are unwrapped. Values read from dictionaries are always optional.
This code unwraps all optional strings
for name in json as! [[String:AnyObject]] {
guard let SeatName = name["SeatName"] as? String,
SeatDesignId = name["SeatDesignId"] as? Int else {
continue
}
self.koltuklar.append("\(SeatName)*\(SeatDesignId)*")
if let blogs = json["SeatDetail"] as? [[String: AnyObject]] {
for blog in blogs {
if let TicketTypeId = blog["TicketTypeId"] as? Int,
TicketTypeName = blog["TicketTypeName"] as? String,
Amount = blog["Amount"] as? Int {
self.koltuklaridler.append("\(SeatDesignId)*\(TicketTypeId)*\(TicketTypeName)*\(Amount)*")
}
}
}
Edit: I updated the casting to the actual types according to the JSON
Now declare both arrays as empty string arrays.
var koltuklar = [String]()
var koltuklaridler = [String]()
and remove the optional binding in the first line
let myStrings = koltuklar[indexPath.row]
print("\(myStrings)")
...
By the way: Your way to "serialize" the strings with asterisks and deserialize it in the table view is very, very clumsy and inefficient. Use a custom class or struct for the data records.
Your problem is that you are creating a string from values from dict without a if let statement so it returns an optional value:
for name in json as! [AnyObject] {
if let SeatName = name["SeatName"],
let SeatDesignId = name["SeatDesignId"] {
self.koltuklar.append("\(SeatName)*\(SeatDesignId)*")
}
if let blogs = json["SeatDetail"] as? [[String: AnyObject]] {
for blog in blogs {
if let TicketTypeId = blog["TicketTypeId"],
let TicketTypeName = blog["TicketTypeName"],
let Amount = blog["Amount"] {
self.koltuklaridler.append("\(SeatDesignId)*\(TicketTypeId)*\(TicketTypeName)*\(Amount)*")
}
}
}
There is a two way of operate optional.
unwrapped using "!" but in this chances of crash if value is nil.
unwrapped using term call "optional binding" using "if let" condition.
if let var = "assigned your optional variable"{
print(var)
}
You will get your variable without optional.
I have an NSMutableArray that contains String values. I have a String variable and I want to check if it is contained in the array or not.
I tried using .contains() with String but it say:
Cannot convert value of type String to expected argument type...
var mutableArray = NSMutableArray() // ["abc", "123"]
var string = "abc"
mutableArray.contains("abc") { // above error in this line
}
Multiple ways to check element existence in NSMutableArray. i.e
if mutableArray.contains("abc")
print("found")
else
print("not found")
or
if contains(mutableArray, "abc")
print("found")
or
if mutableArray.indexOfObject("abc") != NSNotFound
print("found")
If we want to check existence of element according of version of
swift
Swift1
if let index = find(mutableArray, "abc")
print(index)
Swift 2
if let index = mutableArray.indexOf("abc")
print(index)
I do still not understand why you cannot use a native Swift array, but okay.
Two possible solutions are to either use
let contains = mutableArray.contains { $0 as? String == "abc" }
or
let contains = mutableArray.containsObject("abc")
or
let contains = mutableArray.indexOfObject("abc") != NSNotFound
If you would use a native array you could simply do
var array = ["123", "abc"]
let contains = array.contains("abc")
There is an issue that i can't understand and can not find an answer to:
i have this method in a tableViewCOntroller that is calling another viewCOntroller with TableView inside it
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowBook" {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
let books = categoryStore.allCategories[selectedIndexPath.row].books
let destinationVC = segue.destinationViewController as! BookViewController
destinationVC.books = books
}
}
}
Then in the BookViewController i have this just for testing:
var books = [Book]()
override func viewDidLoad() {
super.viewDidLoad()
print(books[0].name)
}
I can see that the books var is an array that is holding books:
class Book {
var name: String = ""
var id: Int = -1
var categoryId: Int = -1
var bookAuthor: String = ""
var level1Name: String = ""
var level2Name: String = ""
var level3Name: String = ""
var level4Name: String = ""
var levels: Int = -1
var orderKey: Int = -1
var viewCount: Int = -1
var viewLevel: Int = -1
var chapters: [AnyObject] = []
}
so i am getting an array of books with 13 key/value pairs dictionary in the books var
when i'm trying to print any thing, lets say:
print(books[0].name)
I get the error:
fatal error: NSArray element failed to match the Swift Array Element type
and i can't understand why...
p.s The transition is working and i can see the next table but then getting the fatal error
Ok, let's start with your error.
fatal error: NSArray element failed to match the Swift Array Element
type
A swift array is complaining that one of it's elements is not what it's expecting. The NSArray element does not match the swift array element.
Now we know that a NSArray can store different types of elements, for example, NSNumber, NSDictionary, NSString... you get the picture. So clearly our issue here is type related.
Now looking at your segue code we can see that we do not actually state what type books is, we let swift's type inference work it out for us. This is where our first sign of the issue occurs. You are expecting an array of [Book] but for some reason you are getting a type of NSArray.
If you make the change from:
let books = categoryStore.allCategories[selectedIndexPath.row].books
let books : [Book] = categoryStore.allCategories[selectedIndexPath.row].books
You will now crash at this line, because you are now stating the type expected.
Full Method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowBook" {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
let books : [Book] = categoryStore.allCategories[selectedIndexPath.row].books
let destinationVC = segue.destinationViewController as! BookViewController
destinationVC.books = books
}
}
}
This is backed up by your debugger console output, it says that you have an array of 13 NSDictionary types.
This means you need to investigate where you populate the [Book] array - i.e. categoryStore.allCategories
OK, This was a beginner problem that might help others.
The scenario: I have a complex JSON: root is object, for each key in the object there is a numeric value (deferent key but the value has the same structure) and the value is the Category Object!
For each category there are many key/Value pairs and one of them is the the Books KEY and the value is array or objects, each one is a book.
The problem: when i deserialised the JSON, i parsed the categories with an array of books, but the books as mentioned in the comments weren't really book instances more array of NSDictionary objects.
So after deserialising the Category i have to add an init method in The Book class to get an NSDictionary and parse it into a Book Object.
After that for each category i have to go over the the NSArray of books and for each one to create an Object go Book and return an Array of Books.
That did the trick and now every thing is working:
API:
// Getting NSArray of NSDictionary that represnets books and convert it into Books array
static func getBooksFromArr(booksArr: NSArray) -> [Book] {
var books = [Book]()
for book in booksArr {
let thisBook = Book(bookDict: book as! NSDictionary)
books.append(thisBook)
print(book)
}
return books
}
// Parsing the category object
private static func CategoryFromJSONObject(json: [String: AnyObject]) -> Category? {
guard let
id = json["Id"] as? Int,
name = json["Name"] as? String,
booksArr = json["Books"] as? NSArray else {
return nil
}
let books = getBooksFromArr(booksArr)
return Category(name: name, id: id, books: books)
}
Book Class:
init(bookDict: NSDictionary){
self.name = (bookDict["Name"] ?? "") as! String
self.id = (bookDict["Id"] ?? -1) as! Int
self.categoryId = (bookDict["CategoryId"] ?? -1) as! Int
self.bookAuthor = (bookDict["BookAuthor"] ?? "") as! String
self.level1Name = (bookDict["Level1Name"] ?? "") as! String
self.level2Name = (bookDict["Level2Name"] ?? "") as! String
self.level3Name = (bookDict["Level3Name"] ?? "") as! String
self.level4Name = (bookDict["Level4Name"] ?? "") as! String
self.levels = (bookDict["Levels"] ?? -1) as! Int
self.orderKey = (bookDict["OrderKey"] ?? -1) as! Int
self.viewCount = (bookDict["ViewCount"] ?? -1) as! Int
self.viewLevel = (bookDict["ViewLevel"] ?? -1) as! Int
self.chapters = (bookDict["Chapters"] ?? []) as! [AnyObject]
}
Now i will have to do the same for the chapters if i want to compose books to hold the chapters.
I didn't write the REST API so don't ask me why they did it as so, but now every thing is working and that is the point of it.
In my case, it was just casting to the wrong array type in the code above the problem...