I have a table that displays a list of pupils and their chosen subjects. The pupil's name are displayed as table section headers and each chosen subject is displayed as table rows for each section. I have a simple class to store the pupil's name as a string and their subjects as an array as follows:
import UIKit
class TableText: NSObject {
var name: String
var subject: [String]
init(name: String, subject: [String]) {
self.name = name
self.subject = subject
}
}
In my custom TableViewCell I have a didSet property observer to track any changes to each pupil's chosen subjects (i.e. I have a textView that the user can click on and modify or change a subject. My didSet is currently as follows:
var tableText: TableText? {
didSet {
let mySubject = myTextView.text
if ((tableText?.subject.contains(mySubject)) != nil) {
}
}
}
My cellForRowAtIndexPath in my TableViewController is as follows:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! myTableViewCell
cell.delegate = self
let pupil = tableTexts[indexPath.section]
cell.myTextView.text = pupil.subject[indexPath.row]
cell.subject = pupil.subject[indexPath.row]
cell.name = pupil.name
print("name: \(cell.name) subject: \(cell.subject)")
return cell
}
I'm puzzled at how to take the data from the tableView of each pupil and pupil's subjects and use didSet to set the pupil's name and list of subjects so I can monitor the textViews and update changes to the model as required. I much appreciate any assistance.
Related
I have a disconcerting issue in that I have a UITableViewCell that does not update the displayed value of its underlying data. To the code:
class ReviewInspectionViewController: UIViewController {
private lazy var locationsDataSource: ReviewInspectionDataSource = ReviewInspectionDataSource(tableView: tableView, delegate: self)
override func viewWillAppear(_ animated: Bool) {
.. retrieve data from Realm
.. process data and place in data object defined as var data : [Any] = []
locationsDataSource.data.append((location.title,data))
}
}
class ReviewInspectionDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let location = data[indexPath.section]
let item = location.content[indexPath.row]
if let item = item as? String {
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ReviewChecklistItemCell.self)
cell.descriptionLabel.text = item
return cell
}
....
}
}
Works fine the first time and the correct string is shown on the screen. I tab to a different view (the underlying UIViewController is in a UITabViewController), make a change and then tab back, I can confirm that the changed data is being set correctly in this line:
cell.descriptionLabel.text = item
I can even print out the value of cell.description.text by adding a line like this:
cell.descriptionLabel.text = item
print("Cell value", cell.descriptionLabel.text)
and it prints out the changed value BUT the screen shows the old value. The UITableViewCell itself is extremely simple:
class ReviewChecklistItemCell: UITableViewCell, NibReusable {
#IBOutlet weak var descriptionLabel: UILabel!
}
The datasource class is loaded from the UIViewController.viewWillAppear method holding onto the UITableView. I have never seen this happen before, thoughts on what the issue is?
It sounds like you are updating the cell description label instead of updating the actual source of the data.
So instead of
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ReviewChecklistItemCell.self)
cell.descriptionLabel.text = item
I would update your data source at that indexPath
dataSource[indexPath.row] = item
tableView.reloadData()
In the code below I'm populating my table with some data. The switches are off which they don't have to be. In the storyboard I defined it as On.
Cell:
var switchHandler: ((Bool)->Void)?
#IBAction func switchChanged(_ sender: UISwitch) {
self.switchHandler?(sender.isOn)
}
View controller:
var selectedCells = Set<IndexPath>()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell") as? SmsTableViewCell
cell?.PhonNumberLbl.text = data![indexPath.section].contacts[indexPath.row]?.phoneNumber
cell?.NameLbl.text = data![indexPath.section].contacts[indexPath.row]?.name
cell?.selectedTF.isOn = (data![indexPath.section].contacts[indexPath.row]?.selected)!
cell?.selectedTF.isOn = self.selectedCells.contains(indexPath)
cell?.switchHandler = { (switchState) in
if switchState {
self.selectedCells.insert(indexPath)
} else {
self.selectedCells.remove(indexPath)
}
}
return cell!
}
Model:
typealias smsModelList = [SmsModel]
struct SmsModel:Codable {
var unitNo:Int?
var unitPlaque:String?
var billText:String?
var contacts:[ContactsModel?]
}
typealias contactlistmodel = [ContactsModel]
struct ContactsModel:Codable
{
var id :Int?
var selected :Bool?
var phoneNumber : String?
var name : String?
}
Does anybody see somthing wrong which turns off the switch?
First of all as you force unwrap the cell anyway do it in the dequeue line to avoid the unnecessary amount of question marks and use the API to return a non-optional cell
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell", for: indexPath) as! SmsTableViewCell
To fix your issue update the selected property of the ContactsModel struct directly and forget the extra selectedCells array. Further declare – at least – selected as non-optional, practically there is no maybe state. And declare also all data source arrays (data / contacts) as non-optional, cellForRow is called only if there is an item at the particular indexPath by default.
struct ContactsModel : Codable {
...
var selected : Bool
...
}
...
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell", for: IndexPath) as! SmsTableViewCell
let contact = data[indexPath.section].contacts[indexPath.row]
cell.PhonNumberLbl.text = contact.phoneNumber
cell.NameLbl.text = contact.name
cell.selectedTF.isOn = contact.selected
cell.switchHandler = { [unowned self] switchState in
// as the structs are value types you have to specify the full reference to the data source array
self.data[indexPath.section].contacts[indexPath.row].selected = switchState
}
Consider to use classes rather than structs in this case then you can shorten the closure
cell.switchHandler = { switchState in
contact.selected = switchState
}
You use both
cell?.selectedTF.isOn = (data![indexPath.section].contacts[indexPath.row]?.selected)!
cell?.selectedTF.isOn = self.selectedCells.contains(indexPath)
so isOn property of the switch is controlled from 2 sides , so you have to decide which line that should be commnented , plus don't depend on storyboard prototype cell setup as because of cell reusing it' ll be changed , if you want to make them all on by default then change the var selectedCells to contain all possible indexPaths and comment the other one
I'm attempting to pass an array of data from the view controller to the collection view cells. My collectionview is currently in a tableview. I have tried using delegation/protocols and creating arrays in the class and have not been able to successfully pass the data to my collectionview.
My code is a follows:
View Controller:
var ageUnder10: [MissingPerson] = []
var age10Plus: [MissingPerson] = []
var age15Plus: [MissingPerson] = []
if let ageRange = ageRange {
switch ageRange {
case .ageUnder10:
let ageUnder10Array = MissingPerson()
ageUnder10Array.title = self.missingPerson.title
ageUnder10Array.desc = self.missingPerson.desc
ageUnder10Array.url = self.missingPerson.url
self.ageUnder10.append(ageUnder10Array)
case .age10Plus:
let age10PlusArray = MissingPerson()
age10PlusArray.title = self.missingPerson.title
age10PlusArray.desc = self.missingPerson.desc
age10PlusArray.url = self.missingPerson.url
self.age10Plus.append(age10PlusArray)
case .age15Plus:
let age15PlusArray = MissingPerson()
age15PlusArray.title = self.missingPerson.title
age15PlusArray.desc = self.missingPerson.desc
age15PlusArray.url = self.missingPerson.url
self.age15Plus.append(age15PlusArray)
}
} else {
print("No valid age found")
}
Tableview Cell:
class TableViewCell: UITableViewCell {
var ageUnder10 = [MissingPerson]()
var age10Plus = [MissingPerson]()
var age15Plus = [MissingPerson]()
}
These values are being populated from an XML url
The categories are being created via scanner, scanning the values of a item in the xml (to create ageRange)
I have titleforheader and header names populated from a separate array in the view controller class
I figured it out, I needed to use a struct to pass the data. Also, create an instance of the array in the tableview class and write a function to fill the collectionView cell.
Example:
CustomTableViewCell:
customArray: [CustomArray]()
func configureCollectionCell(with array: [CustomArray]) {
self.customArray = customArray
}
ViewController Class:
var customArray = [CustomArray]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as? CustomTableViewCell {
cell.configureCollectionCell(with: customArray)
return cell
}
So I have a lot of code already in place for this but I am getting some errors:
My Current code is :
func createGroupMessagesButton() {
dismissViewControllerAnimated(true) {
let user = self.tableView.indexPathsForSelectedRows
self.messagesController2?.showChatLogController(user)
}
}
The code above is meant to dismiss the current view controller, and pass all of the data into a function on the next view. That functions code is:
func showChatLogController(user: User) {
let chatLogController = ChatLogController(collectionViewLayout: UICollectionViewFlowLayout())
chatLogController.user = user
chatLogController.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(chatLogController, animated: true)
}
The function above then pushes to another controller with that data that was passed down to the function above.
The only problem is that when I first try to pass the data I get an error that states:
Cannot convert value of type [NSIndexPath]? to expected type argument User
PS: User is an array that I have created.
This is my user array:
class User: NSObject {
var id: String!
var fullName: String!
var email: String!
var userPhoto: String!
var homeAddress: NSArray!
var schoolOrWorkAddress: String!
}
To sum up my issue I am having trouble passing the data of multiple selected table view cells.
If you would like to know how I pass one selected cells data this is how:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView.allowsMultipleSelectionDuringEditing != true {
dismissViewControllerAnimated(true) {
let user = self.users[indexPath.row]
self.messagesController?.showChatLogController(user)
}
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! UserCell
let user = users[indexPath.row]
cell.textLabel?.text = user.fullName
cell.detailTextLabel?.text = user.email
if let userPhoto = user.userPhoto {
cell.profileImageView.loadImageUsingCacheWithUrlString(userPhoto)
}
return cell
}
An NSIndexPath is a construct to get a path to a specific row in a specific section.
The self.tableView.indexPathsForSelectedRows returns you a list of selected rows, so you'll then have to loop through them and use the row-attribute to find the corresponding user in your complete list of users. Also note that you most likely want to pass an Array of User instead of just one.
This is conceptional code of the top of my head, that should lead you in the correct direction.
func createGroupMessagesButton() {
dismissViewControllerAnimated(true) {
let selectedUserRows = self.tableView.indexPathsForSelectedRows
var selectedUsers = [User]
for let selectedUserRow in selectedUserRows {
selectedUsers.append(self.users[selectedUserRow.row]!)
}
self.messagesController2?.showChatLogController(selectedUsers)
}
}
I am trying to load data from a structure to table view cell, I created a custom cell with three label in it. I have three text field in the view controller and a add button I want that when I fill these three text field and press add it will store these three values in a structure and reload the data of table view. Structure is in other file.
Here is code for structure in DataMaster.swift
struct jobData
{
var CompanyName:Array<String> = ["ram"]
var job:Array<String> = ["shyam"]
var desc:Array<String> = ["dfdf"]
}
Code for addButton function
#IBAction func addButtonTapped(sender: AnyObject) {
var company = txtCompName.text
var job = txtJob.text
var description = txtDesc.text
data.CompanyName.append(company)
data.desc.append(description)
data.job.append(job)
self.jobTableView.reloadData()
print(data.CompanyName)
txtCompName.resignFirstResponder()
txtJob.resignFirstResponder()
txtDesc.resignFirstResponder()
}
The problem is in this code
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath:indexPath) as jobTableViewCell
cell.compLabel.text = data.CompanyName[indexPath.row]
cell.jobLabel.text = data.job[indexPath.row]
cell.descLabel.text = data.desc[indexPath.row]
return cell
}
when it reaches to this code to load data in table it crashes
Thread 1:EXC_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0)
Here below is code.
struct jobData
{
var CompanyName:String!
var job:String!
var desc:String!
}
Take an array as var datas = [jobData]()
Now in Action method
#IBAction func addButtonTapped(sender: AnyObject) {
var company = txtCompName.text
var job = txtJob.text
var description = txtDesc.text
let dataObject = jobData(company: company, job: job, desc: description)
datas.append(dataObject)
self.jobTableView.reloadData()
txtCompName.resignFirstResponder()
txtJob.resignFirstResponder()
txtDesc.resignFirstResponder()
}
Now in cellForRowAtIndex method
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath:indexPath) as! jobTableViewCell
let data = datas[indexPath.row]
if let companyName = data.CompanyName {
cell.compLabel.text = companyName
}
if let job = data.job {
cell.jobLabel.text = job
}
if let descr = data.desc {
cell.descLabel.text = descr
}
return cell
}
in numberofRowsInSection method return datas.count
Check why data.CompanyName is empty and make sure all text field will have text.