0

I have two lists of subjects in my app. The user can choose several subjects from the second list to add these to the first one. I now have problems with the adding process.

The cells from the second list are described by this:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("AddSubjectCell", forIndexPath: indexPath) as! AddSubjectCell

    let subject = Subjects[indexPath.row] as Subject
    let Semester = "\(subject.semester)"

    cell.nameLabel.text = subject.name

    cell.semesterLabel.text = "Semester"

    cell.semesterNumberLabel.text = Semester

    return cell

}

and take their data from this array:

var addSubjectsData = [ Subject(name: "A", semester: 1), Subject(name: "B", semester: 2), Subject(name: "C", semester: 3), Subject(name: "D", semester: 5), Subject(name: "E", semester: 6) ]

If the user checkmarked the cell, this part of a function is activated:

 if cell.accessoryType == UITableViewCellAccessoryType.Checkmark {
        selectedCellsData.append(Subject(name: selectedCellsData.last!.name, semester: selectedCellsData.last!.semester))

Hereby,

    selectedCellsData.name = cell.nameLabel.text!
    selectedCellsData.semester = cell.semesterNumberLabel.text!.toInt()!

The array that I am trying to append is defined as var selectedCellsData = [ Subject(name: "Initial Subject", semester: 0)].

On the screen of the second list is a done button. If it is pressed, this action is being activated:

 @IBAction func saveSubjectDetail(segue:UIStoryboardSegue) {

    subjectsData.append(Subject(name: selectedCellsData.last!.name, semester: selectedCellsData.last!.semester))

The selected cells' data should then be added to the array var subjectsData = [ Subject(name: "Investments", semester: 1), Subject(name: "Statistics", semester: 1), Subject(name: "Studium Universale", semester: 2) ].

Trying to run the simulator, I receive the error "[Subject] does not have a member named 'name'". Though, as it can be seen in my array, there is a member called 'name'.

Also, when I declared the class 'Subject', I also added 'name':

class Subject: NSObject {
    var name: String
    var semester: Int

    init(name: String, semester: Int) {
        self.name = name
        self.semester = semester
        super.init()
    }
}

I would appreciate any help on how to solve this error.

Elina
  • 79
  • 8
  • 1
    `[Subject]` is not `Subject`, it's an array of `Subject`s. – The Paramagnetic Croissant Aug 11 '15 at 12:57
  • As is probably apparent now, the `selectedCellsData.append(Subject(name: selectedCellsData.name, semester: selectedCellsData.semester))` syntax makes no sense. That says grab a `name` and `semester` from `selectedCellsData` (which is actually an array of `Subject` objects, so it's not clear which one you meant to grab), and append it back to the same `selectedCellsData`. The question in my mind is what were you trying to do with that line. It's hard to tell you how to fix it without knowing what you were trying to accomplish. – Rob Aug 11 '15 at 13:25
  • I created two lists of subjects and want to enable the user to add the selected subjects from the second list to the first. I have been trying to directly add the cells data which are distracted from another array with the same structure to the array from which the data for the first list are taken but as that did not work, I am now trying to work with an "in-between" dataset which is the one above. The lines here are the process of adding the cells' data to the "in-between"-array – Elina Aug 11 '15 at 13:37

2 Answers2

1

selectedCellsData is an array of Subject. So you should pick an item from the array if that's what you need. So probably something like this

selectedCellsData.last.name.text = cell.nameLabel.text 
Raymond
  • 3,630
  • 2
  • 19
  • 22
  • That's closer. But (a) `last` is optional; and (b) `name` doesn't have `text` property (only `nameLabel` does). If you wanted to do this, it would be `selectedCellsData.last?.name = cell.nameLabel.text`. – Rob Aug 11 '15 at 13:31
  • Thank you for your answer. I am pretty new to Swift so I am not quite sure what exactly you mean, esp. with `last.`. FYI, I am creating a list of cells from one array and try to add the data from cells which were selected by the user to another array. Above is the step of adding the selected cells data into the array. – Elina Aug 11 '15 at 13:33
  • @Elina - You have an array of `Subject` objects, and you're saying that you want to update the `name` of one of them. But which one?!? Raymond was guessing you wanted to update the last one in the array (which seems like a bit of a leap, but maybe that's me). I'd suggest you step back and tell us what you're really trying to do. With all due deference to Raymond who is just trying to help you out, it seems like answers provided without a broader understanding of what you're trying to accomplish are premature. – Rob Aug 11 '15 at 13:36
  • 1
    @Rob I edited the post. I hope it is now more understandable. – Elina Aug 11 '15 at 13:50
1

You said:

I created two lists of subjects and want to enable the user to add the selected subjects from the second list to the first. I have been trying to directly add the cells data which are distracted from another array with the same structure to the array from which the data for the first list are taken but as that did not work, I am now trying to work with an "in-between" dataset which is the one above. The lines here are the process of adding the cells' data to the "in-between"-array

So, let's say you have two arrays, one called allSubjects and another called selectedSubjects. And let's say you select a row in a table (and thus have an indexPath.row which indicates which row was selected in the table showing us allSubjects). Then you could do:

selectedSubjects.append(allSubjects[indexPath.row])

--

Unfortunately, this now begs the question if you unselect a row, how do you remove the Subject from selectedSubjects. You'd probably have to go down the road of making Subject conform to Equatable, use filter method, etc.

Possibly easier is to just keep track of the selected rows

var selectedRows = Set<Int>()

Then, when you select a row, you'd add a row entry:

selectedRows.insert(indexPath.row)

And when you unselect a row, you'd remove it

selectedRows.remove(indexPath.row)

For example, you could have cellForRowAtIndexPath set the appropriate accessory based upon the presence of the row in selectedRows:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("AddSubjectCell", forIndexPath: indexPath) as! AddSubjectCell

    let subject = allSubjects[indexPath.row]

    cell.nameLabel.text = subject.name
    cell.semesterLabel.text = "Semester"
    cell.semesterNumberLabel.text = "\(subject.semester)"

    cell.accessoryType = selectedRows.contains(indexPath.row) ? .Checkmark : .None

    return cell
}

And then when the user selects a row, toggle its checkmark:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if selectedRows.contains(indexPath.row) {
        selectedRows.remove(indexPath.row)
    } else {
        selectedRows.insert(indexPath.row)
    }

    tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}

If you wanted, when you're all done, to get an array of selected subjects, you could then do something like:

var selectedSubjects = [Subject]()
for row in selectedRows {
    selectedSubjects.append(allSubjects[row])
}

--

Another approach is to add a selected property to the Subject class:

class Subject {
    var name: String
    var semester: Int
    var selected = false

    init(name: String, semester: Int) {
        self.name = name
        self.semester = semester
    }
}

Then cellForRowAtIndexPath can look at this property:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("AddSubjectCell", forIndexPath: indexPath) as! AddSubjectCell

    let subject = allSubjects[indexPath.row]

    cell.nameLabel.text = subject.name
    cell.semesterLabel.text = "Semester"
    cell.semesterNumberLabel.text = "\(subject.semester)"

    cell.accessoryType = subject.selected ? .Checkmark : .None

    return cell
}

The didSelectRowAtIndexPath can just toggle it:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    allSubjects[indexPath.row].selected = !allSubjects[indexPath.row].selected

    tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}

And then when you want the selected subjects, you can just filter them:

let selectedSubjects = allSubjects.filter { $0.selected }

There are lots of ways of skinning the cat.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • is the first thing you described with `indexPath.row` also possible for when the user selects several cells? I understood it that `indexPath.row` only works for one cell or do I have mistake in my knowledge there? – Elina Aug 11 '15 at 14:06
  • Yes, `indexPath.row` is a single integer for a single row. That's why I suggested building a set of integers (`Set`), which will be a set containing the rows for all of the selected rows. – Rob Aug 11 '15 at 14:11
  • Ok, so if I change my code to what you describe above, I still do not know how I can append the array `subjectsData` with the selected data? – Elina Aug 11 '15 at 14:24
  • You could just loop through the set of selected rows and build your separate array of corresponding subjects. See example at the end of the answer. There are tons of different ways of handling this, but hopefully this illustrates the basic idea. – Rob Aug 11 '15 at 14:31
  • But I only want to have these changes saved when the 'Done' button is pressed. If I do as you suggest, I would have to add the the loop "on a different page" of the code, thus, it would not be executed when the `@IBAction func saveSubjectDetail` is activated but already before. Thus, I would have the problem that I could not remove the changed the user made, if the 'cancel' button is chosen and not 'Done'. – Elina Aug 11 '15 at 14:41
  • Fine. That's the advantage of the separate `selectedRows` approach (where you only build the array of selected subjects when you hit the `Done` button). I was just showing you alternatives. You should do what's right for your app. – Rob Aug 11 '15 at 14:43
  • I really appreciate your help, I am just too new to Swift to understand how to execute what you suggest. Do you mean that what you described above should all be part of the `IBAction func saveSubjectDetail`? Because it is on a "different page" and the references would not work. I am not sure how to use your suggested alternative. I understand you alternatives, but not how to connect them with the cancel or done buttons. – Elina Aug 11 '15 at 14:58
  • I presume your view controller that has this table view in which you're selecting these classes has a "done" button. The `IBAction` linked to that button could build the array of selected subjects and return it to the caller. In terms of how to pass data back, perhaps see http://stackoverflow.com/a/9736559/1271826. – Rob Aug 11 '15 at 15:08
  • Independent of which of your alternatives I choose, I still stand before the initial problem that the `subjectsData` array cannot be appended because the type of it does not match with `selectedSubjects`. Excluding the `@IBAction` for the moment, I am still not able to solve my code to append the first list with new cells in any way, which is my big problem. – Elina Aug 11 '15 at 16:08
  • 1
    Assuming `subjectsData` is a `[Subjects]`, then you can append the objects with `subjectsData += selectedSubjects` or completely replace it with `subjectsData = selectedSubjects`. – Rob Aug 11 '15 at 17:19