I'm trying to create a view controller with multiple pickerviews using the following code:
import UIKit
import Firebase
import FirebaseAuth
class DormViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var dormName: UIPickerView!
#IBOutlet weak var entryway: UIPickerView!
#IBOutlet weak var roomNumber: UIPickerView!
#IBOutlet weak var nextButton: UIButton!
var ref: DatabaseReference!
let dorms = ["Adams", "Apley", "Cabot", "Canaday", "Currier", "DeWolfe 10", "Dewolfe 20", "Dunster", "Eliot", "Fairfax", "Grays", "Greenough", "Hollis", "Holworthy", "Hurlbut", "Inn at Harvard", "Kirkland", "Leverett Towers", "Leverett McKinlock Hall", "Lionel", "Lowell", "Massachusetts Hall", "Mather Lowrise", "Mather Tower", "Matthews", "Mower", "New Quincy", "Pennypacker", "Pforzheimer", "Pforzheimer Jordan North", "Pforzheimer Jordan South", "Stone Hall", "Stoughton", "Straus", "Thayer", "Weld", "Wigglesworth", "Winthrop" ]
let entrances = ["N/A", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
let rooms = ["N/A", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"]
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
dormName = UIPickerView()
entryway = UIPickerView()
roomNumber = UIPickerView()
dormName.delegate = self
dormName.dataSource = self
entryway.delegate = self
entryway.dataSource = self
roomNumber.delegate = self
roomNumber.dataSource = self
dormName.tag = 0
entryway.tag = 1
roomNumber.tag = 2
// Do any additional setup after loading the view.
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView.tag == 1 {
return dorms[row]
}
else if pickerView.tag == 2 {
return entrances[row]
}
else {
return rooms[row]
}
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView.tag == 1 {
return dorms.count
}
else if pickerView.tag == 2 {
return entrances.count
}
else {
return rooms.count
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let user = Auth.auth().currentUser
if let user = user {
let uid = user.uid
var entry = entrances[row]
var num = rooms[row]
if entrances[row] == "N/A" {
entry = ""
}
if rooms[row] == "N/A" {
num = ""
}
self.ref.child("Dorms").child(uid).setValue(["Location": dorms[row] + " " + entry + "-" + num ])
}
else {
print("error")
}
}
#IBAction func nextButton(_ sender: Any) {
performSegue(withIdentifier: "tohomepage", sender: self)
}
}
However, I'm getting an error:
fatal error: unexpectedly found nil while unwrapping an Optional value
In my viewDidLoad() function, when I make the call:
dormName.delegate = self
I've tried deleting the delegate and dataSource calls, and the same error still happens. I also tried deleting the tags and referencing the picker views by their names, as in:
pickerview == "dormName"
But then I get an index out of range error.
What am I doing wrong?
when you drag UIPickerView from xib or storyboad you don't need call some init :
dormName = UIPickerView()
entryway = UIPickerView()
roomNumber = UIPickerView()
delete it , and try again
In your didSelectRow you do not check for the picker instance anymore and just access the data of all arrays by the same row index. This will cause an index out of range error as soon as you pick an item with an index that goes beyond the smallest array length (in this case, entrances).
Also, looks like you have your tags/indices wrong. E.g. you set the tag of dormName to 0, but in your code you access the dorm data when the tag equals 1 (which in fact refers to the entryway picker).
Related
I have an array starting from 1 to 100 and I have to print element if the number is divisible by 4 it should print the letter "A" and if the number is divisible by 5 it should print the letter "B" and if it is divisible by both then "AB" I want to make a scalable solution if in future I want to add number divisible by 8 should print "C" and divisible by 4 & 8 should print "AC", by 5&8 should print "BC" and if all three then "ABC"
desired output:
1
2
3
A
B
6
7
C
9
B
11
AB
13
14
...
I wrote this
for number in 1...100 {
if number.isMultiple(of: 4) && !number.isMultiple(of: 5){
print("A"
} else if !number.isMultiple(of: 4) && number.isMultiple(of: 5){
print("B")
} else if number.isMultiple(of: 4) && number.isMultiple(of: 5){
print("AB")
} else {
print(number)
}
}
Please provide a scalable solution to keep adding If-else is not a good option.
You were pretty close but you don't need the else conditions. Just add the character to the string if it matches another condition:
for number in 1...100 {
var string = ""
if number.isMultiple(of: 4) { string.append("A") }
if number.isMultiple(of: 5) { string.append("B") }
if number.isMultiple(of: 8) { string.append("C") }
print(string.isEmpty ? number : string)
}
Using a dictionary to store the characters:
let dict = [
4: "A",
5: "B",
8: "C"
]
for number in 1...100 {
var string = ""
for (key, character) in dict where number.isMultiple(of: key) {
string.append(character)
}
print(string.isEmpty ? number : string)
}
Note that dictionary is an unordered collection. If you need the characters to be sorted you would need to sort the dictionary by its values before iterating its key value pairs:
let sortedDict = dict.sorted(by: { $0.value < $1.value })
for number in 1...100 {
var string = ""
for (key, character) in sortedDict where number.isMultiple(of: key) {
string.append(character)
}
print(string.isEmpty ? number : string)
}
Here it is, instead of using if-else, you can just add up whenever you need
var stringArray = [String]()
for number in 0...100 {
stringArray.append(String(number))
}
// stringArray = ["0","1", "2", "3",....,"99", "100"]
// Adding a zero before to compare with the index
stringArray = stringArray.enumerated().map({ index, item in
var value = item
if index % 4 == 0 {
value = Int(item) == nil ? item + "A": "A"
}
return value
})
stringArray = stringArray.enumerated().map({ index, item in
var value = item
if index % 5 == 0 {
value = Int(item) == nil ? item + "B": "B"
}
return value
})
stringArray = stringArray.enumerated().map({ index, item in
var value = item
if index % 8 == 0 {
value = Int(item) == nil ? item + "C": "C"
}
return value
})
stringArray.removeFirst()
print(stringArray)
Result::
"1", "2", "3", "A", "B", "6", "7", "AC", "9", "B", "11", "A", "13", "14", "B", "AC", "17", "18", "19", "AB", "21", "22", "23", "AC", "B", "26", "27", "A", "29", "B", "31", "AC", "33", "34", "B", "A", "37", "38", "39", "ABC", "41", "42", "43", "A", "B", "46", "47", "AC", "49", "B", "51", "A", "53", "54", "B", "AC", "57", "58", "59", "AB", "61", "62", "63", "AC", "B", "66", "67", "A", "69", "B", "71", "AC", "73", "74", "B", "A", "77", "78", "79", "ABC", "81", "82", "83", "A", "B", "86", "87", "AC", "89", "B", "91", "A", "93", "94", "B", "AC", "97", "98", "99", "AB"
if you just want [Any] type then just
var resultArray = [Any]()
resultArray = stringArray.map({ number in
if let num = Int(number) { return num }
else { return number }
})
print(resultArray)
my raw json data maybe mislead you. The keys array were not always matched its value at the same index. So I rewrote my data to reflect my intentions.
Assume we have a table view to show songs with its json:
{
"albums": [
{
"title": "A",
"id": "174172",
"artistName": "Person X"
},
{
"title": "B",
"id": "19201827",
"artistName": "Person Y"
},
{
"title": "C",
"id": "1927",
"artistName": "Person Z"
}
],
"songs": [
{
"name": "Song A",
"albumName": "A",
"albumId": "174172",
"duration": 180
},
{
"name": "Song B",
"albumName": "A",
"albumId": "174172",
"duration": 200
},
{
"name": "Song C",
"albumName": "B",
"albumId": "19201827",
"duration": 216
},
{
"name": "Song D",
"albumName": "C",
"albumId": "1927",
"duration": 216
}
]
}
My schemas like this:
struct Album: Decodable {
let title: String
let id: String
let artistName: String
}
struct Song: Decodable {
let name: String
let albumName: String
let albumId: String
let duration: Int
}
The view controller fake code like this:
class ViewController: UIViewController {
var songs: [Song] = []
var albums: [Album] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return songs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "SongCell", for: indexPath) as! SongCell
let song = songs[indexPath.row]
let album = albums.first { $0.id == song.albumId }
cell.updateUI(withSong: song, album: album)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let song = songs[indexPath.row]
let album = albums.first { $0.id == song.albumId }
pushDetailSongViewController(song, album)
}
func pushDetailSongViewController(_ song: Song, _ album: Album?) {
}
}
When we have too many songs with albums, let album = albums.first { $0.id == song.albumId } is place with terrible performance problem.
So what data structure should we use here to handle updateing performance?
After parsing both keys and values, you could combine the two arrays to a dictionary and then have your table view's data source be that dictionary.
First, make your Song struct conform to the Hashable protocol:
struct Song: Hashable {
Create an array for both albums and songs:
var albums: [Album] = []
var songs: [Song] = []
Then, reduce the songs array to a dictionary as follows:
let data = songs.reduce([Album: Song]()) { (result, song) -> [Album: Song] in
guard let album = albums.first(where: { $0.id == song.albumID }) else { return result }
return result.merging([album: song], uniquingKeysWith: { (first, _) in first })
}
I tested this with two demo arrays:
let albums = [Album(id: "1", name: "one"), Album(id: "2", name: "two"), Album(id: "3", name: "three")]
let songs = [Song(albumID: "1", name: "ONE"), Song(albumID: "2", name: "TWO"), Song(albumID: "3", name: "THREE")]
Those turn data into:
[
<Album id: "1", name: "one"> : <Song albumID: "1", name: "ONE">,
<Album id: "2", name: "two"> : <Song albumID: "2", name: "TWO">,
<Album id: "3", name: "three">: <Song albumID: "3", name: "THREE">
]
Extra Credit
If you want all songs for each album, you have to make data [Album: [Song]]:
let data = albums.reduce([Album: [Song]]()) { (result, album) -> [Album: [Song]] in
let _songs = songs.filter({ $0.albumID == album.id })
guard !_songs.isEmpty else { return result }
return result.merging([album: _songs], uniquingKeysWith: { (first, _) in first })
}
With the following arrays:
let albums = [Album(id: "1", name: "one"), Album(id: "2", name: "two"), Album(id: "3", name: "three")]
let songs = [Song(albumID: "1", name: "ONE"), Song(albumID: "2", name: "TWO"), Song(albumID: "3", name: "THREE"),
Song(albumID: "1", name: "ONE-1"), Song(albumID: "1", name: "ONE-2"), Song(albumID: "3", name: "THREE-1")]
...you will get:
[
<Album name: three, id: 3>: [
<Song name: THREE, albumID: 3>
<Song name: THREE-1, albumID: 3>
],
<Album name: one, id: 1>: [
<Song name: ONE, albumID: 1>,
<Song name: ONE-1, albumID: 1>,
<Song name: ONE-2, albumID: 1>
],
<Album name: two, id: 2>: [
<Song name: TWO, albumID: 2>
]
]
You should create a struct like below after the JSON parsing has been completed.
struct DataSet {
let id: String
let name: String
let value: String
}
Moreover looking at your json, it seems like objects at same index of Key and Value arrays are same with respect to id and key . So, at the time of combining both the arrays, if you iterate one array, you will be knowing the index of another array (O(1)) . Hence time complexity of merging will be O(n).
If you don't want to change too much, maybe a mapDictionary will help:
let keyMaps = [String : String](uniqueKeysWithValues: keys.map{($0.id, $0.name)})
keyNamesInSequenceSameWithValues = values.map{ keyMaps[$0.key]! )
I'm trying to parse a JSON file to my code.
So, far I have managed to do so except for one key (genre) that has an array of values.
I want to know how can I parse it so I can present it in a UILabel?
JSON data in file:
[{
"title": "Dawn of the Planet of the Apes",
"image": "http://api.androidhive.info/json/movies/1.jpg",
"rating": 8.3,
"releaseYear": 2014,
"genre": ["Action", "Drama", "Sci-Fi"]
},
{
"title": "District 9",
"image": "http://api.androidhive.info/json/movies/2.jpg",
"rating": 8,
"releaseYear": 2009,
"genre": ["Action", "Sci-Fi", "Thriller"]
},
{
"title": "Transformers: Age of Extinction",
"image": "http://api.androidhive.info/json/movies/3.jpg",
"rating": 6.3,
"releaseYear": 2014,
"genre": ["Action", "Adventure", "Sci-Fi"]
},
{
"title": "X-Men: Days of Future Past",
"image": "http://api.androidhive.info/json/movies/4.jpg",
"rating": 8.4,
"releaseYear": 2014,
"genre": ["Action", "Sci-Fi", "Thriller"]
},
{
"title": "The Machinist",
"image": "http://api.androidhive.info/json/movies/5.jpg",
"rating": 7.8,
"releaseYear": 2004,
"genre": ["Drama", "Thriller"]
},
{
"title": "The Last Samurai",
"image": "http://api.androidhive.info/json/movies/6.jpg",
"rating": 7.7,
"releaseYear": 2003,
"genre": ["Action", "Drama", "History"]
},
{
"title": "The Amazing Spider-Man 2",
"image": "http://api.androidhive.info/json/movies/7.jpg",
"rating": 7.3,
"releaseYear": 2014,
"genre": ["Action", "Adventure", "Fantasy"]
},
{
"title": "Tangled",
"image": "http://api.androidhive.info/json/movies/8.jpg",
"rating": 7.9,
"releaseYear": 2010,
"genre": ["Action", "Drama", "Sci-Fi"]
},
{
"title": "Rush",
"image": "http://api.androidhive.info/json/movies/9.jpg",
"rating": 8.3,
"releaseYear": 2013,
"genre": ["Animation", "Comedy", "Family"]
},
{
"title": "Drag Me to Hell",
"image": "http://api.androidhive.info/json/movies/10.jpg",
"rating": 6.7,
"releaseYear": 2009,
"genre": ["Horror", "Thriller"]
},
{
"title": "Despicable Me 2",
"image": "http://api.androidhive.info/json/movies/11.jpg",
"rating": 7.6,
"releaseYear": 2013,
"genre": ["Animation", "Comedy", "Family"]
},
{
"title": "Kill Bill: Vol. 1",
"image": "http://api.androidhive.info/json/movies/12.jpg",
"rating": 8.2,
"releaseYear": 2003,
"genre": ["Action", "Crime"]
},
{
"title": "A Bug's Life",
"image": "http://api.androidhive.info/json/movies/13.jpg",
"rating": 7.2,
"releaseYear": 1998,
"genre": ["Animation", "Adventure", "Comedy"]
},
{
"title": "Life of Brian",
"image": "http://api.androidhive.info/json/movies/14.jpg",
"rating": 8.9,
"releaseYear": 1972,
"genre": ["Comedy"]
},
{
"title": "How to Train Your Dragon",
"image": "http://api.androidhive.info/json/movies/15.jpg",
"rating": 8.2,
"releaseYear": 2010,
"genre": ["Animation", "Adventure", "Family"]
}]
Movie class:
import UIKit
class Movie {
var title = ""
var image = ""
var rating = 0.0
var releaseYear = 0
init(object: [String: AnyObject]) {
self.title = object["title"] as! String
self.image = object["image"] as! String
self.rating = object["rating"] as! Double
self.releaseYear = object["releaseYear"] as! Int
}
init() {
}
}
MovieCell Class:
import UIKit
import SDWebImage
class MovieCell: UITableViewCell {
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var releaseYearLbl: UILabel!
#IBOutlet weak var ratingLbl: UILabel!
#IBOutlet weak var movieImage: UIImageView!
#IBOutlet weak var genreLbl: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
func createCell(movie: Movie) {
self.titleLbl.text = movie.title
self.releaseYearLbl.text = "\(movie.releaseYear)"
self.ratingLbl.text = "\(movie.rating) ⭐️"
movieImage.layer.cornerRadius = 10.0
movieImage.clipsToBounds = true
self.movieImage.sd_setImage(with: URL(string: movie.image))
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Swift 3.0
Just analyse below code and apply it to your issue
The Movie class becomes,
class Movie {
var title = ""
var image = ""
var rating = 0.0
var releaseYear = 0
var genre:String?
init(object: [String: AnyObject]) {
self.title = object["title"] as! String
self.image = object["image"] as! String
self.rating = object["rating"] as! Double
self.releaseYear = object["releaseYear"] as! Int
let genreArray = object["genre"] as! [String]
/*Whatever you want do here with genre. I thought you need whole array to one readable String*/
for strings in genreArray{
self.genre = self.genre == nil ? strings : self.genre!+" "+strings
}
}
}
Class that had UITableView
The global variable of the class is,
var movieData = Array<Movie>()
The JSON parse will becomes,
if let json = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as? Array<Dictionary<String, AnyObject>>{
for object in json{
self.movieData.append(Movie(object: object))
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
UITableView datasource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for:indexPath)
let tableObject:Movie = self.movieData[indexPath.row]
cell.textLabel?.text = tableObject.genre //This way you will be get all values and apply it to whatever you want...
return cell
}
OUTPUT:-
I've started building a very simple app which has two screens. The first has two UIbuttons. One button plays a random sound using the code below in my first view controller (ViewController.swift) and the other button modally presents a second screen.
On the second screen there are also two UIbuttons one to play another random sound and the other to go back to the first screen.
Playing a random sound on the first screen works perfectly.
For the second screen I have created a brand new ViewController called PageOneViewController.swift and made sure that the class is assigned correctly in IB. This second view controller contains a carbon copy clone of the code from the second page except with changes in names to avert conflict and confusuion. However any attempt to play a sound on this second page results in a Unrecognized Selector error and I cannot figure out why.
The error message is as follows:
2016-10-17 18:04:57.749 NewApp[28750:2719569] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '- [NewApp.PageOneViewController buttonPressed2WithSender:]: unrecognized selector sent to instance 0x7fe79070b8a0'
And it stops on this line (which I realise isn't much help):
class AppDelegate: UIResponder, UIApplicationDelegate {
I am connecting the buttons manually via the inspector and using 'Touch Up Inside" as the touch method.
I cannot get my head around this at all. In the good old Objective-C days I would run into this issue when I'd named a file incorrectly or one of the sound files was missing from the bundle but I've checked everything over and over again and cannot work out whats going on.
Code for ViewController.swift:
import AVFoundation
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var mainButton1: UIButton!
var arrayOfSounds = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44",
"45", "46", "47", "48", "49", "50", "51" , "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82"]
var audioPlayer : AVAudioPlayer?
func setupAudioPlayer(file: NSString, type: NSString){
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
}
#IBAction func buttonPressed(sender: AnyObject){
let range: UInt32 = UInt32(arrayOfSounds.count)
let number = Int(arc4random_uniform(range))
self.setupAudioPlayer(arrayOfSounds[number], type: "wav")
self.audioPlayer?.play()
}
}
Code for PageOneViewController.swift:
import AVFoundation
import UIKit
class PageOneViewController: UIViewController {
#IBOutlet weak var mainButtonNew: UIButton!
var arrayOfSounds = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44",
"45", "46", "47", "48", "49", "50", "51" , "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82"]
var audioPlayer : AVAudioPlayer?
func setupAudioPlayer(file: NSString, type: NSString){
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
}
#IBAction func buttonPressedNew(sender: AnyObject){
let range: UInt32 = UInt32(arrayOfSounds.count)
let number = Int(arc4random_uniform(range))
self.setupAudioPlayer(arrayOfSounds[number], type: "wav")
self.audioPlayer?.play()
}
}
What may have happened is in storyboard something may have a double reference like a button referencing two IBOutlets or to two IBActions. You may have forgotten to remove a reference from them check their connections in the connection inspector or ctrl clicking on one. Have you tried placing print statements in your code or breakpoints to see exactly where it crashes?
Well, I still don't know exactly what caused this issue but a desperate attempt to fix it resulted in me updating to Swift 3 from 2.3, fixed the issue immediately.
I have created a struct with a library and have initialised that struct in another struct. Now I want to use the resulting struct in a Table View. However, the struct currently doesn't work properly. I've tried to find out why, but seem to be stuck.
It looks like the issue is that the library doesn't get translated properly to the struct. For in instance, when I do a count on an instance of the struct, using the following code:
var personalDetailsStructExtra: [PersonalDetailsStruct] = []
personalDetailsStructExtra.count
It returns 0, while it should be 5 (See code below, there are 5 entries into the dictionary):
struct PersonalDetailsStructLibrary {
let library = [
[
"title": "Country",
"icon": "country.pdf",
"questions": ["Belgium", "France", "Germany", "Netherlands", "Sweden", "UK", "USA"]
],
[
"title": "Age",
"icon": "age.pdf",
"questions": ["1", "2", "3", "4", "5", "6", "7"]
],
[
"title": "Gender",
"icon": "gender.pdf",
"questions": ["Male", "Female", "Other"]
],
[
"title": "Height",
"icon": "height.pdf",
"questions": ["1", "2", "3", "4", "5", "6", "7"]
],
[
"title": "Weight",
"icon": "weight.pdf",
"questions": ["1", "2", "3", "4", "5", "6", "7"]
],
] }
And
struct PersonalDetailsStruct {
var title: String?
var icon: UIImage?
var questions: [String] = []
init(index: Int) {
let personalDetailsStructLibrary = PersonalDetailsStructLibrary().library
let personalDetailsDictionary = personalDetailsStructLibrary[index]
title = personalDetailsDictionary["title"] as! String!
let iconNamePD = personalDetailsDictionary["icon"] as! String!
icon = UIImage(named: iconNamePD!)
questions += personalDetailsDictionary["artists"] as! [String]
} }
As you can see in the code I want use the struct to fill up a label (title), image (icon) and UITextView with UIPickerView (questions) in my table view.
Since it doesn't work, I'm looking for either:
A: Feedback on how to make this code work in a tableview
B: Whether I should use another method to populate the dynamic cells in my tableview
You have to initialize the personalDetailsStructExtra array, only then you would see the required count.
var personalDetailsStructExtra = [PersonalDetailsStruct]()
PersonalDetailsStructLibrary().library.count
let count = PersonalDetailsStructLibrary().library.count
for i in 0..<count {
personalDetailsStructExtra.append(PersonalDetailsStruct(index: i))
}
personalDetailsStructExtra.count // 5
A better option is to use the Library struct to construct and maintain all model objects.
struct PersonalDetailDataSource {
let library = [
[
"title": "Country",
"icon": "country.pdf",
"questions": ["Belgium", "France", "Germany", "Netherlands", "Sweden", "UK", "USA"]
],
[
"title": "Age",
"icon": "age.pdf",
"questions": ["1", "2", "3", "4", "5", "6", "7"]
],
[
"title": "Gender",
"icon": "gender.pdf",
"questions": ["Male", "Female", "Other"]
],
[
"title": "Height",
"icon": "height.pdf",
"questions": ["1", "2", "3", "4", "5", "6", "7"]
],
[
"title": "Weight",
"icon": "weight.pdf",
"questions": ["1", "2", "3", "4", "5", "6", "7"]
],
]
var personalDetails = [PersonalDetail]()
init() {
loadData()
}
mutating private func loadData() {
for i in 0..<library.count {
let personalDetailsDictionary = library[i]
let title = personalDetailsDictionary["title"] as! String!
let iconName = personalDetailsDictionary["icon"] as! String!
let questions = personalDetailsDictionary["questions"] as! [String]
personalDetails.append(PersonalDetail(title: title, iconName: iconName, questions: questions))
}
}
}
struct PersonalDetail {
var title: String?
var icon: UIImage?
var questions: [String] = []
init(title: String, iconName: String, questions: [String]) {
self.title = title
if let icon = UIImage(named: iconName) {
self.icon = icon
}
self.questions = questions
}
}
PersonalDetailDataSource().personalDetails.count // count: 5