Order multiple TableViewCell types by TimeStamp - ios

What I want to do:
Order all my TableViewCells from most recent to the oldest.
What is my problem:
I can order Cells from the same type by Time, though I fail at ordering them all in the same section by one common value (the time).
Here is my code:
import UIKit
import Firebase
class popularViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet var table: UITableView!
// var models = [PhotoPost]()
var texttt = [TextPost]()
var phots = [PhotoPost]()
var mixed = [MixedPhoto]()
var switchy = [Any]()
override func viewDidLoad() {
super.viewDidLoad()
gettingPosts()
table.register(popularTableViewCell.nib(), forCellReuseIdentifier: popularTableViewCell.identifier)
table.register(featuredTableViewCell.nib(), forCellReuseIdentifier: featuredTableViewCell.identifier)
table.register(textTableViewCell.nib(), forCellReuseIdentifier: textTableViewCell.identifier)
table.register(mixedTableViewCell.nib(), forCellReuseIdentifier: mixedTableViewCell.identifier)
table.delegate = self
table.dataSource = self
self.table.estimatedRowHeight = 225
self.table.rowHeight = UITableView.automaticDimension
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0{
return mixed.count
}
else if section == 1{
return phots.count
}
else{
return texttt.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0{
let cell = tableView.dequeueReusableCell(withIdentifier: mixedTableViewCell.identifier, for: indexPath) as! mixedTableViewCell
cell.configure(with: self.mixed[indexPath.row])
return cell
}
else if indexPath.section == 1{
let cell = tableView.dequeueReusableCell(withIdentifier: popularTableViewCell.identifier, for: indexPath) as! popularTableViewCell
cell.configure(with: self.phots[indexPath.row])
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: self.texttt[indexPath.row])
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "commentsVC")
vc.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(vc, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"]
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
let pid = data["postID"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text as! String, postID: pid)
self.texttt.append(dasDing)
self.texttt.sort(by: { $0.timestampName > $1.timestampName })
}
if typ == 1{ // Text + Picture post
let Mixed = MixedPhoto(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text as! String, postImageName: pic as! String, postID: pid)
self.mixed.append(Mixed)
self.mixed.sort(by: { $0.timestampName > $1.timestampName })
}
if typ == 2{ // Picture Post
let Foto = PhotoPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, postImageName: pic as! String, postID: pid)
self.phots.append(Foto)
self.phots.sort(by: { $0.timestampName > $1.timestampName })
}
if typ == 3{ // Text + Video Post
}
if typ == 4{ // Video Post
}
}
}
self.table.reloadData()
}
}
}
struct PhotoPost {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let postImageName: String
let postID: String
}
struct TextPost {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let textName: String
let postID: String
}
struct MixedPhoto {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let textName: String
let postImageName: String
let postID: String
}
Note
Right now I ordered every Cell type in its own section to be able to display them all, but this isnt a long-term solution for me. I want to have them all in only one section.

Here is another solution:
After reading Paul & Rikh's answer, I thought about this other solution.
You can have one Post model defined as follows:
struct Post {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let postImageName: String?
let textName: String?
let postID: String
}
Notice how postImageName and textName are both optionals. That will help you distinguish among post types.
Next, you want to declare a variable allPosts of type [Post]:
var allPosts = [Post]()
And you need to feed it to your tableView:
func numberOfSections(in tableView: UITableView) -> Int {
return 1 // You only need one section. You can't omit this function btw.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allPosts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = allPosts[indexPath.row]
if post.postImageName != nil && post.textName != nil {
let cell = tableView.dequeueReusableCell(withIdentifier: mixedTableViewCell.identifier, for: indexPath) as! mixedTableViewCell
cell.configure(with: post)
return cell
}
else if post.postImageName != nil {
let cell = tableView.dequeueReusableCell(withIdentifier: popularTableViewCell.identifier, for: indexPath) as! popularTableViewCell
cell.configure(with: post)
return cell
}
else if post.textName != nil {
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: post)
return cell
}
return UITableViewCell() // default: return empty cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "commentsVC")
vc.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(vc, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Finally, inside your gettingPosts function, you can assign each item to allPosts, and before reloading your tableView, you can sort allPosts by timestamp (and don't forget to call reloadData from the main thread):
self.allPosts.sort(by: { $0.timestampName > $1.timestampName })
DispatchQueue.main.async {
self.tableView.reloadData()
}
EDIT: Inside your gettingPosts function you need to feed the values to your Post struct rather than the previous model you had.
let post = Post(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text as? String, postImageName: pic as? String, postID: pid)
self.allPosts.append(dasDing)
One more remark: there has to be a better way to do this. Why not decode your JSON instead? This can become messy if you got several types of posts. If you post your JSON structure, maybe we can help you figure out a better solution.

You can do that, but you have to declare an array allPosts of type Any:
var allPosts = [Any]()
And you need to feed it to your tableView:
func numberOfSections(in tableView: UITableView) -> Int {
return 1 // You only need one section. You can't omit this function btw.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allPosts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let mixedPhoto = allPosts[indexPath.row] as? MixedPhoto {
let cell = tableView.dequeueReusableCell(withIdentifier: mixedTableViewCell.identifier, for: indexPath) as! mixedTableViewCell
cell.configure(with: mixedPhoto)
return cell
}
else if let photoPost = allPosts[indexPath.row] as? PhotoPost {
let cell = tableView.dequeueReusableCell(withIdentifier: popularTableViewCell.identifier, for: indexPath) as! popularTableViewCell
cell.configure(with: photoPost)
return cell
}
else if let textPost = allPosts[indexPath.row] as? TextPost {
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: textPost)
return cell
}
return UITableViewCell() // default: return empty cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "commentsVC")
vc.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(vc, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Finally, inside your gettingPosts function, you can assign each item to allPosts since it is of type any, and before reloading your tableView, you can sort allPosts by timestamp (and don't forget to call reloadData from the main thread):
self.allPosts.sort(by: { $0.timestampName > $1.timestampName })
DispatchQueue.main.async {
self.tableView.reloadData()
}

You have three different lists maintained. To order all of them, you will have to combine them in one list. Now all of your models have majority of the data common. You can combine them into one protocol and have all your subsequent models conform to that protocol. Something like this:
protocol Post{
var numberOfComments: Int { get set }
var username: String {get set}
var timestampName: String {get set}
var userImageName: String {get set}
}
struct PhotoPost : Post{
var numberOfComments: Int
var username: String
var timestampName: String
var userImageName: String
let postImageName: String
let postID: String
}
struct TextPost : Post { ... }
struct MixedPhoto : Post { ... }
And inside your UIViewController you should have one list.
var oneListToRuleThemAll = [Post]()
func gettingPosts(){
snapshot.documentChanges.forEach{
//blah blah blah
oneListToRuleThemAll.append(PhotoPost(...))
}
//sort by timestamp and reload the table!
}
Or as Paul suggests in his comment, you can go the class (inheritance) route too! To avoid having to write the variables again inside each subclass (But you cannot use structs with that and will have to change everything to classes).

Related

how to get main tableview index in nested tableview in swift

I am new in swift and I am not able to get main tableview index on nested tableview button click
my code is like this
extension Section2QuestionsViewController: UITableViewDataSource, UITableViewDelegate
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrFinancialYears.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let strCellID = "Section2QuestionCell"
var cell = tableView.dequeueReusableCell(withIdentifier: strCellID) as? Section2QuestionCell
if cell == nil
{
tableSection2.register(Section2QuestionCell.self, forCellReuseIdentifier: strCellID)
cell = tableSection2.dequeueReusableCell(withIdentifier: strCellID) as? Section2QuestionCell
}
let dictAppStats = arrFinancialYears[indexPath.row] as? [String:Any]
cell?.lblQuestionTitle.text = "\(indexPath.row + 1). \(dictAppStats?["question"] as? String ?? "")"
cell?.arrCourses = [Any]()
cell?.arrCourses = dictAppStats?["options"] as? [Any] ?? []
cell?.tableInsideHeightConstraints.constant = CGFloat(28 * (cell?.arrCourses.count ?? 0))
cell?.tableInside.reloadData()
return cell!
}
}
Inside tableview
extension Section2QuestionCell: UITableViewDataSource, UITableViewDelegate
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrCourses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InsideSection2QuestionCell", for: indexPath) as! InsideSection2QuestionCell
cell.selectionStyle = .none
let dictFeeStats = arrCourses[indexPath.row] as? [String:Any]
cell.lblOption.text = dictFeeStats?["option_value"] as? String
cell.btnOption.tag = indexPath.item
cell.btnOption.addTarget(self, action: #selector(btnOptionClick), for: .touchUpInside)
cell.lblAnswerKey.text = dictFeeStats?["answer_key"] as? String
cell.lblQuestionId.text = dictFeeStats?["question_id"] as? String
cell.lblAnswerId.text = dictFeeStats?["option_id"] as? String
return cell
}
}
Button Click
#objc func btnOptionClick(_ sender: UIButton)
{
let index = IndexPath(row: sender.tag, section: 0)
let cell: InsideSection2QuestionCell = tableInside.cellForRow(at: index) as! InsideSection2QuestionCell
let QuestionId = cell.lblQuestionId.text
let AnswwerId = cell.lblAnswerId.text
print(QuestionId as Any)
print(AnswwerId as Any)
Globalnewdict = ["option":AnswwerId as Any,"question":QuestionId as Any]
Globalindexvalue = sender.tag
NotificationCenter.default.post(name: Notification.Name(rawValue: "disconnectPaxiSockets"), object: nil)
tableInside.reloadData()
}
I am not able to get main tableview index path value on button. Is there is any way to get main tableview index path value on button click.
Please help
Thanks in Advance!
You could add an index path object in your Section2QuestionCell
var parentTableIndexPath : IndexPath!
Then set the value in cellForRowAt tableview delegate method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let strCellID = "Section2QuestionCell"
var cell = tableView.dequeueReusableCell(withIdentifier: strCellID) as? Section2QuestionCell
if cell == nil {
tableSection2.register(Section2QuestionCell.self, forCellReuseIdentifier: strCellID)
cell = tableSection2.dequeueReusableCell(withIdentifier: strCellID) as? Section2QuestionCell
}
cell.parentTableIndexPath = indexPath
let dictAppStats = arrFinancialYears[indexPath.row] as? [String:Any]
cell?.lblQuestionTitle.text = "\(indexPath.row + 1). \(dictAppStats?["question"] as? String ?? "")"
cell?.arrCourses = [Any]()
cell?.arrCourses = dictAppStats?["options"] as? [Any] ?? []
cell?.tableInsideHeightConstraints.constant = CGFloat(28 * (cell?.arrCourses.count ?? 0))
cell?.tableInside.reloadData()
return cell!
}
Since btnOptionClick() is in same class, you can directly access the parent table Index path in your button action.
In
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let strCellID = "Section2QuestionCell"
var cell = tableView.dequeueReusableCell(withIdentifier: strCellID) as? Section2QuestionCell
if cell == nil
{
tableSection2.register(Section2QuestionCell.self, forCellReuseIdentifier: strCellID)
cell = tableSection2.dequeueReusableCell(withIdentifier: strCellID) as? Section2QuestionCell
}
let dictAppStats = arrFinancialYears[indexPath.row] as? [String:Any]
cell?.lblQuestionTitle.text = "\(indexPath.row + 1). \(dictAppStats?["question"] as? String ?? "")"
cell?.arrCourses = [Any]()
cell?.arrCourses = dictAppStats?["options"] as? [Any] ?? []
cell?.tableInsideHeightConstraints.constant = CGFloat(28 * (cell?.arrCourses.count ?? 0))
cell?.tableInside.tag = indexPath.row
cell?.tableInside.reloadData()
return cell!
}
In ButtonClick
#objc func btnOptionClick(_ sender: UIButton)
{
let index = IndexPath(row: sender.tag, section: 0)
let cell: InsideSection2QuestionCell = tableInside.cellForRow(at: index) as! InsideSection2QuestionCell
let QuestionId = cell.lblQuestionId.text
let AnswwerId = cell.lblAnswerId.text
print(QuestionId as Any)
print(AnswwerId as Any)
Globalnewdict = ["option":AnswwerId as Any,"question":QuestionId as Any]
Globalindexvalue = tableInside.tag
NotificationCenter.default.post(name: Notification.Name(rawValue: "disconnectPaxiSockets"), object: nil)
tableInside.reloadData()
}
In cellForRowAt I set indexpath.row to tableview.tag.
Then on button click I set tableview.tag to global indexpath.

How to Sort TableViewCells by date string

As shown below I want to sort my TableViewCells by the date. For this I have the time which is also called timestampName.
Right before I reload the data, I tried to sort it, but somehow this has no effect. It also throws me a warning, that I dont use the result of the sorted by. I understand this, but I dont know how to fix that.
import UIKit
import Firebase
class popularViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet var table: UITableView!
// var models = [PhotoPost]()
var texttt = [TextPost]()
override func viewDidLoad() {
super.viewDidLoad()
gettingPosts()
table.register(popularTableViewCell.nib(), forCellReuseIdentifier: popularTableViewCell.identifier)
table.register(featuredTableViewCell.nib(), forCellReuseIdentifier: featuredTableViewCell.identifier)
table.register(textTableViewCell.nib(), forCellReuseIdentifier: textTableViewCell.identifier)
table.delegate = self
table.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return texttt.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: self.texttt[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"] as! String
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text)
self.texttt.append(dasDing)
self.texttt.sorted(by: { $0.timestampName < $1.timestampName }) //WARNING: Result of call to 'sorted(by:)' is unused
self.table.reloadData()
}
struct TextPost {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let textName: String
}
Use sort instead of sorted. The sorted method returns a new sorted array, on the other hand, the sort method sorts the array on which it was called.
self.texttt.sort(by: { $0.timestampName < $1.timestampName })
This should also work, using sorted:
self.texttt = self.texttt.sorted(by: { $0.timestampName < $1.timestampName })

Selected row from each section of UITableView ( Multiple Selection )

I have used tableview(grouped).
So i need to select one row from the each section of UITableviewSection.
So for that i have tableview and one submit button .So i need to check when i click on the submit button i need to check whether i have selected one row from the each section ,if not then show alert as not selected the section number.How to check?
This is my data.
{
"data":[
{
"question": "Gender",
"options": ["Male","Female"]
},
{
"question": "How old are you",
"options": ["Under 18","Age 18 to 24","Age 25 to 40","Age 41 to 60","Above 60"]
},
{
"question": "I am filling the Questionnaire for?",
"options": ["Myself","Mychild","Partner","Others"]
}
]
}
QuestionModel:-
class QuestionListModel: NSObject {
var selected = false
var dataListArray33:[NH_OptionsModel] = []
var id:Int!
var question:String!
var buttontype:String!
var options:[String]?
var v:String?
var optionsModelArray:[OptionsModel] = []
init(dictionary :JSONDictionary) {
guard let question = dictionary["question"] as? String,
let typebutton = dictionary["button_type"] as? String,
let id = dictionary["id"] as? Int
else {
return
}
if let options = dictionary["options"] as? [String]{
print(options)
print(options)
for values in options{
print(values)
let optionmodel = OptionsModel(values: values)
self.optionsModelArray.append(optionmodel)
}
}
self.buttontype = typebutton
self.question = question
self.id = id
// print(self.dataListArray33)
}
}
optionModel:-
class OptionsModel: NSObject {
var isSelected:Bool? = false
var v:String?
var values:String?
init(values:String) {
self.values = values
print( self.values)
}
ViewModel:-
func numberOfSections(tableView: UITableView) -> Int{
print((datasourceModel.dataListArray?.count)!)
return (datasourceModel.dataListArray?.count)!
}
func titleForHeaderInSection(atsection section: Int) -> NH_QuestionListModel {
return datasourceModel.dataListArray![section]
}
func numberOfRowsIn(section:Int) -> Int {
print( datasourceModel.dataListArray?[section].optionsModelArray.count ?? 0)
return datasourceModel.dataListArray?[section].optionsModelArray.count ?? 0
// return self.questionsModelArray?[section].optionsModelArray.count ?? 0
}
func datafordisplay(atindex indexPath: IndexPath) -> NH_OptionsModel{
print(datasourceModel.dataListArray![indexPath.section].optionsModelArray[indexPath.row])
return datasourceModel.dataListArray![indexPath.section].optionsModelArray[indexPath.row]
}
func question(answer:String) {
print(questions)
questions.append(answer)
print(questions )
}
func questionlist(answer:String) {
print( questionlist )
questionlist.append(answer)
print( questionlist )
}
func answer(answer:String) {
answers.append(answer)
print(answers)
}
and finally viewController:-
func numberOfSections(in tableView: UITableView) -> Int {
return questionViewModel.numberOfSections(tableView: tableView)
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let identifier = "HeaderCell"
var headercell: questionheader! = tableView.dequeueReusableCell(withIdentifier: identifier) as? questionheader
if headercell == nil {
tableView.register(UINib(nibName: "questionheader", bundle: nil), forCellReuseIdentifier: identifier)
headercell = tableView.dequeueReusableCell(withIdentifier: identifier) as? NH_questionheader
}
headercell.setReviewData(reviews:questionViewModel.titleForHeaderInSection(atsection:section))
return headercell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return questionViewModel.numberOfRowsIn(section: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "Cell"
var cell: QuestionListCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? QuestionListCell
if cell == nil {
tableView.register(UINib(nibName: "QuestionListCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? NH_QuestionListCell
}
cell.contentView.backgroundColor = UIColor.clear
let questionsModel = questionViewModel.titleForHeaderInSection(atsection:indexPath.section)
print(questionsModel.buttontype)
questionViewModel.button = questionsModel.buttontype
cell.setOptions(Options1: questionViewModel.datafordisplay(atindex: indexPath))
print("Section \(indexPath.section), Row : \(indexPath.row)")
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
print("Section \(indexPath.section), Row : \(indexPath.row)")
let cell = tableview.cellForRow(at: indexPath) as? NH_QuestionListCell
let model = questionViewModel.datafordisplay(atindex: indexPath)
print(model.isSelected)
cell?.setOptions(OptionsSelected:questionViewModel.datafordisplay(atindex: indexPath))
print(model.isSelected)
questionViewModel.isselected = model.isSelected!
let section = indexPath.section
let index = indexPath.row
print(section)
print(index)
if !questionViewModel.selectedIndexPaths.contains(indexPath) {
questionViewModel.selectedIndexPaths.append(indexPath)
print(questionViewModel.selectedIndexPaths.append(indexPath))
let questionModel = questionViewModel.titleForHeaderInSection(atsection: section)
print(questionModel.question)
questionViewModel.question = questionModel.question
questionViewModel.questionlist(answer: questionViewModel.question!)
let cell = tableview.cellForRow(at: indexPath) as? NH_QuestionListCell
let model = questionViewModel.datafordisplay(atindex: indexPath)
print(model.values)
questionViewModel.answer(answer: model.values!)
let value: Int = questionModel.id
let string = String(describing: value)
//let x: Int? = Int(model.id)
questionViewModel.question_id = string
questionViewModel.question(answer: questionViewModel.question_id!)
print(questionModel.id)
// append the selected index paths
} // if indexPath.section == section {
// questionViewModel.indexPath(indexPaths: index)
// }
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = questionViewModel.selectedIndexPaths.index(of: indexPath) {
print(index)
questionViewModel.selectedIndexPaths.remove(at: index)
}
}
According to this i got the output .
But i have button action in viewcontroller.
#IBAction func forward(_ sender: AnyObject) {
}
In this button action i need to check whether from each section did i selected one row or not .if not show alert .How to do
my current didselect method :-
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let cell = tableview.cellForRow(at: indexPath) as? NH_QuestionListCell
let model = questionViewModel.datafordisplay(atindex: indexPath)
print(model.isSelected)
cell?.setOptions(OptionsSelected:questionViewModel.datafordisplay(atindex: indexPath))
print(model.isSelected)
questionViewModel.isselected = model.isSelected!
let section = indexPath.section
let index = indexPath.row
print(section)
print(index)
if !questionViewModel.selectedIndexPaths.contains(indexPath) {
questionViewModel.selectedIndexPaths.append(indexPath)
print(questionViewModel.selectedIndexPaths.append(indexPath))
let questionModel = questionViewModel.titleForHeaderInSection(atsection: section)
print(questionModel.question)
questionViewModel.question = questionModel.question
questionViewModel.questionlist(answer: questionViewModel.question!)
let cell = tableview.cellForRow(at: indexPath) as? NH_QuestionListCell
let model = questionViewModel.datafordisplay(atindex: indexPath)
print(model.values)
questionViewModel.answer(answer: model.values!)
let value: Int = questionModel.id
let string = String(describing: value)
//let x: Int? = Int(model.id)
questionViewModel.question_id = string
questionViewModel.question(answer: questionViewModel.question_id!)
print(questionModel.id)
}
I have 3 array
According to this didselect method:-
ex:-for section 1 :-i selected 1st row so the data append as below.
questionlist:["How r u?"]
answelist:["fine"]
But suppose i think that i need 2nd indexpath ,so i need to remove the previous appended data from arrays and append the current data .As below:
questionlist:["How r u?"]
answelist:["not well"]
And next for section 2 : i selected 1st indexpath.row data .then that data is append.So i need to get as below:-
questionlist:["How r u?","Gender"]
answelist:["not well","Male"]
Here selecting i think that i need the 2nd option then remove the added indexpath.row data from array and show as:-
questionlist:["How r u?","Gender"]
answelist:["not well","Female"]
Such way how to set?
you can update your model based on the selection like
"data":[
{
"question": "Gender",
"options": ["Male","Female"],
"optionSelected": "Male"
}
]
and on Submit , check data for selections
The table view has a property to get selected index paths. You can use all native components for that. What you need is to deselect an item at index path where one is already selected in a certain section. You also just need to then check that the number of selected index paths is the same as number of arrays in your data source.
Check something like this:
var dataSource: [[Any]]!
var tableView: UITableView!
func didSelectRowAt(_ indexPath: IndexPath) {
guard let selectedPaths = tableView.indexPathsForSelectedRows else { return } // We need to have selected paths
guard selectedPaths.contains(indexPath) == false else { return } // The same cell being selected
let previouslySelectedCellIndexPaths: [IndexPath] = selectedPaths.filter { $0.section == indexPath.section && $0 != indexPath } // Getting all selected index paths within this section
previouslySelectedCellIndexPaths.forEach { tableView.deselectRow(at: $0, animated: true) } // Deselect waht was previously selected
}
/// Will return array of selected objects only if all sections have a selected index
///
/// - Returns: A result array
func getSelectionData() -> [Any]? {
guard let selectedPaths = tableView.indexPathsForSelectedRows else { return nil } // We need to have selected paths
guard selectedPaths.count == dataSource.count else { return nil } // This should prevent missing selections assuming all index paths are unique in sections
return selectedPaths.map { dataSource[$0.section][$0.row] } // Map selected index paths back to objects
}
I tried to use kind of minimum code to show all of this. It is all commented so you can see row by row what goes on.
You might want to check is all sections are unique the second method but it is not needed if the first one is always used.
You can store selected indexPath in an array. OnClick of submit just loop through array and check either at least one element is from each section.
FYI : indexPath contains section info also.
Declare an mutable array and allocate in viewDidLoad.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[anArray addObject:indexPath];
}
on Submit action follow this, you can improvise based on your requirement
-(void)onSubmitAction{
[anArray addObject:indexPath];
NSMutableArray *countOfSection=[[NSMutableArray alloc]init];
for (NSIndexPath*indexPath in anArray ) {
if(![anArray containsObject:indexPath.section])
[countOfSection addObject:indexPath.section];
}
if(countOfSection.count == self.tableview.numberOfSections){
//write your code
}else{
// show alert
}
}
Step 1 : Create Global Variable
var selectedIndexPaths = [IndexPath]()
Step 2: Add UITableView Property
tableView.allowsMultipleSelection = true
Step 3 : Implement the delegate methods
//On Selection
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedIndexPathAtCurrentSection = selectedIndexPaths.filter({ $0.section == indexPath.section})
for indexPath in selectedIndexPathAtCurrentSection {
tableView.deselectRow(at: indexPath, animated: true)
if let indexOf = selectedIndexPaths.index(of: indexPath) {
selectedIndexPaths.remove(at: indexOf)
}
}
selectedIndexPaths.append(indexPath)
}
// On DeSelection
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = selectedIndexPaths.index(of: indexPath) {
selectedIndexPaths.remove(at: index)
}
}
Step 4: Getting Selected IndexPaths with sections
#IBAction func forward(sender:Any){
let totalSections = questionViewModel.numberOfSections(tableView: tableView)
for section in 0..<totalSections {
if (selectedIndexPaths.filter({ $0.section == section}).count >= 1) {
continue
} else {
// Show alert
print("Please select item at",(section))
return
}
}
}

Tapped cell in UITableView returns label text from last cell out of an array not the chosen one

I have fixed my earlier problem and have now worked out where the main problem is, I am pulling in a json array with alamofire but am not sure how to properly move the data from one viewcontroller to another. If I hardcode the array with var name = ["Hello", "Goodbye"] I can get it to work but am not sure how to do it with the json. Thank you to any and all help.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let URL_GET_DATA = "http://www.localnewsplus.com.au/ios/service.php"
#IBOutlet weak var tableViewHeroes: UITableView!
var heroes = [Hero]()
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return heroes.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
let hero: Hero
hero = heroes[indexPath.row]
cell.labelName.text = hero.name
cell.labelTeam.text = hero.team
Alamofire.request(hero.imageUrl!).responseImage { response in
if let image = response.result.value {
cell.heroImage.image = image
}
}
//cell.labelName.text = name[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(URL_GET_DATA).responseJSON { response in
if let json = response.result.value {
let heroesArray : NSArray = json as! NSArray
for i in 0..<heroesArray.count{
self.heroes.append(Hero(
name: (heroesArray[i] as AnyObject).value(forKey: "st_heading") as? String,
team: (heroesArray[i] as AnyObject).value(forKey: "st_modified") as? String,
imageUrl: (heroesArray[i] as AnyObject).value(forKey: "imageurl") as? String
))
}
self.tableViewHeroes.reloadData()
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "articleViewController") as? articleViewController
vc?.article_st_heading = name[indexPath.row]
self.navigationController?.pushViewController(vc!, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
There are 2 ways to do this
Try to get data from the array which you used in cellForRow to populate data
Let text = someArray[indexPath. Row]
Get the cell instead of the create new one in didSelect method
Let cell = table. CellForRowAt[indexPath ]
Let text = cell.text

Convert JSON to a Array with struct

I am trying to make a IOS app that is a home automation thing. I am using TableViewCell to display information.
My problem is that I have no idea how to get JSON to an Array with struct because I have to have struct I think.
My JSON is:
[{"namea":"TV","statea":"up_tv"},{"namea":"test","statea":"test"}]
My code is:
struct cellData {
let nameLabel : String!
let stateLabel : String!
}
class Main: UITableViewController {
var array = [cellData]()
override func viewDidLoad() {
array = [cellData(nameLabel: "tv", stateLabel: "up_tv"),
cellData(nameLabel: "tv", stateLabel: "down_tv")]
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("TableViewCell", owner: self, options: nil)?.first as! TableViewCell
cell.nameLabel.text = array[indexPath.row].nameLabel
cell.stateLabal.text = array[indexPath.row].stateLabel
return cell
}
You need jsonDecoder
struct cellData : Decodable {
let nameLabel : String
let stateLabel : String
enum CodingKeys:String,CodingKey {
case nameLabel = "namea"
case stateLabel = "statea"
}
}
//
let str = """
[{"namea":"TV","statea":"up_tv"},{"namea":"test","statea":"test"}]
"""
do {
let cellArr = try JSONDecoder().decode([cellData].self, from: str.data(using:.utf8)!)
print(cellArr) //// check this
} catch {
}
//
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "id") as TableViewCell
}

Resources