UITableViewCell are repeating and lost its data while scrolling - ios
I have created a Form , in which I am using Textfields and TextField Dropdown. While I am scrolling the TableView textfields values got lost and textfield dropdown values set to default automatically.
Here is my code :
enum TextFieldTags:Int {
case ProspectName = 10, VisitDate, VisitType, Industry, Source, DissSummary, Requirements, FollowUpDate, AnticipatedDealValue, Probability, ExpectedClosingDate, Status, NextStep, Emp, Rev, AddressLine1, AddressLine2, Country, States, City, Zip, Mobile,Fax
}
var prospectName:String?, visitDate:String? , visitType:String?, industry:String?, source:String?, dissSummary:String?, requirements:String?, followUpDate:String?, anticipatedDealValue:String?, probability:String?, expectedClosingDate:String?, status:String?, nextStep:String?, emp:String?, rev:String?, addressLine1:String?, addressLine2:String?, country, statesVal:String?, cityVal:String?, zip:String?, mobile:String?,fax:String?
var currentLeadMaster:WizLeadMaster?
// MARK: - Section Data Structure
struct Section {
var name: String!
var options: [Any]!
var collapsed: Bool!
var optionItem:[Any]!
var icon:UIImage!
init(name: String, options: [Any], collapsed: Bool = false,optionItem:[Any],icon:UIImage) {
self.name = name
self.options = options
self.collapsed = collapsed
self.optionItem = optionItem
self.icon = icon
}
}
// MARK: - Properties
#IBOutlet weak var tblLeadDetail:UITableView?
var dataSource:NSArray = NSArray(objects: "Lead Summary","Address","Products","Contacts","Attachment","Follow Up","Remarks")
var sections = [Section]()
lazy var visitArr:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMVisittype)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var leadStatus:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMLeadStatus)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var leadSource:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMLeadsource)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var industries:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMCustomerType)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var countries:NSArray = {
let visitTypes = Handler.countriesList()
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizCountryList
let dict = ["system":(visitModel.lable! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var states:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMTerritory)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
func setStatesForCountry(_ country:String) -> Void{
}
func filterValue(_ arr:NSArray,key:String) -> Any {
let predicate = NSPredicate(format: "self == %#", key)
let filteredArr = arr.filtered(using: predicate)
if (filteredArr.count == 0) {
return ""
}
return filteredArr[0]
}
// MARK: - ViewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
let barButtonItem = UIBarButtonItem(image:UIImage(named:"save1") , style: UIBarButtonItemStyle.done, target: self, action: #selector(saveLeadInfo))
navigationItem.rightBarButtonItem = barButtonItem
sections = [
Section(name: "Lead Summary", options: [["title":"Prospect Name","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.customer_name as Any,"tag":TextFieldTags.ProspectName.rawValue],
["title":"Visit Date","type":"date","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.visited_date as Any,"tag":TextFieldTags.VisitDate.rawValue],
["title":"Visit Type","type":"dropdown","options":visitArr.value(forKeyPath: "system") as! Array<String>,"value":filterValue(visitArr.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.visit_type)!),"tag":TextFieldTags.VisitType.rawValue],
["title":"Industry","type":"dropdown","options":industries.value(forKeyPath: "system"),"value":filterValue(industries.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.type)!),"tag":TextFieldTags.Industry.rawValue],
["title":"Source","type":"dropdown","options":leadSource.value(forKeyPath: "system"),"value":filterValue(leadSource.value(forKeyPath: "value") as! NSArray , key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.source)!),"tag":TextFieldTags.Source.rawValue],
["title":"Discussion Summary","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.summary_disc as Any,"tag":TextFieldTags.DissSummary.rawValue],
["title":"Requirements","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.requirements as Any ,"tag":TextFieldTags.Requirements.rawValue],
["title":"Follow-Up Date","type":"date","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.followup_date as Any,"tag":TextFieldTags.FollowUpDate.rawValue],
["title":"Anticipated Deal Value","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.anticipated_deal_value as Any,"tag":TextFieldTags.AnticipatedDealValue.rawValue],
["title":"Probabilty","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.probability as Any,"tag":TextFieldTags.Probability.rawValue],
["title":"Expected Closing Date","type":"date","value":currentLeadMaster?.expected_closing_date as Any,"tag":TextFieldTags.ExpectedClosingDate.rawValue],
["title":"Status","type":"dropdown","options":leadStatus.value(forKeyPath: "system"),"value":filterValue(leadStatus.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.status)!),"tag":TextFieldTags.Status.rawValue],
["title":"Next Step","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.next_activity_planned as Any,"tag":TextFieldTags.NextStep.rawValue],
["title":"Emp","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.no_of_emp as Any,"tag":TextFieldTags.Emp.rawValue],
["title":"Rev","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.annual_revenue as Any,"tag":TextFieldTags.Rev.rawValue]], collapsed: true, optionItem: [],icon:UIColor.clear.getImage(size: CGSize(width: 30, height: 30))),
Section(name: "Address", options: [["title":"Address Line 1","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline1 as Any ,"tag":TextFieldTags.AddressLine1.rawValue],
["title":"Address Line 2","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.AddressLine2.rawValue],
["title":"Select Country","type":"dropdown","options":countries.value(forKeyPath: "system") as! Array<String>,"value":filterValue(visitArr.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.visit_type)!),"tag":TextFieldTags.Country.rawValue],
["title":"Select States","type":"dropdown","options":visitArr.value(forKeyPath: "system") as! Array<String>,"value":filterValue(visitArr.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.visit_type)!),"tag":TextFieldTags.States.rawValue],
["title":"Select City","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.City.rawValue],
["title":"Zip Code","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.Zip.rawValue],
["title":"Mobile","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.Mobile.rawValue],
["title":"Fax","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.Fax.rawValue]
], collapsed: true, optionItem: [],icon:UIColor.clear.getImage(size: CGSize(width: 30, height: 30))),
Section(name: "Product", options: [], collapsed: true, optionItem: [],icon:UIImage.init(named: "add")!),
Section(name: "Contacts", options: [], collapsed: true, optionItem: [],icon:UIImage.init(named: "add")!),
Section(name: "Attachment", options: [], collapsed: true, optionItem: [],icon:UIImage.init(named: "attach")!),
]
let headerNib = UINib(nibName: "WizHeaderView", bundle: nil)
tblLeadDetail?.register(headerNib, forHeaderFooterViewReuseIdentifier: "headerLeads")
let textInputCell = UINib(nibName: "TextInputCell", bundle: nil)
tblLeadDetail?.register(textInputCell, forCellReuseIdentifier: "textinputcell")
let dropDownInputCell = UINib(nibName: "DropDownInputCell", bundle: nil)
tblLeadDetail?.register(dropDownInputCell, forCellReuseIdentifier: "dropDownInputCell")
let dropDownDateCell = UINib(nibName: "DropDownDateCell", bundle: nil)
tblLeadDetail?.register(dropDownDateCell, forCellReuseIdentifier: "dropDownDateCell")
tblLeadDetail?.reloadData()
}
func saveLeadInfo() -> Void {
}
// MARK: - TableView Delegates & Datasource
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let optionsArray = sections[section].options
return optionsArray!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let optionsArray = sections[indexPath.section].options
let dict = optionsArray?[indexPath.row] as! Dictionary<String,Any>
if (dict["type"] as! String == "dropdown") {
let cell = tableView.dequeueReusableCell(withIdentifier: "dropDownInputCell") as! DropDownInputCell
cell.txtInput.isOptionalDropDown = false
cell.txtInput.itemList = dict["options"] as! [String]
cell.lblText.text = dict["title"] as? String
cell.txtInput.selectedItem = dict["value"] as? String
cell.txtInput.tag = (dict["tag"] as? NSInteger)!
cell.backgroundColor = UIColor.clear
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.delegate = self
return cell
}
else if (dict["type"] as! String == "input") {
let cell = tableView.dequeueReusableCell(withIdentifier: "textinputcell") as! TextInputCell
cell.lblText.text = dict["title"] as? String
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.backgroundColor = UIColor.clear
cell.txtInput.text = dict["value"] as? String
cell.txtInput.tag = (dict["tag"] as? NSInteger)!
cell.delegate = self
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "dropDownDateCell") as! DropDownDateCell
cell.lblText.text = dict["title"] as? String
cell.txtInput.dropDownMode = IQDropDownMode.datePicker
if dict["value"] as? String == "" {
cell.txtInput.setDate(Date(), animated: true)
}
else{
cell.txtInput.selectedItem = dict["value"] as? String
}
cell.txtInput.tag = (dict["tag"] as? NSInteger)!
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.backgroundColor = UIColor.clear
cell.delegate = self
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// if indexPath.row == 0 {
// performSegue(withIdentifier: "leadsummary", sender:nil)
// }
// else{
// performSegue(withIdentifier: "leadaddress", sender:nil)
// }
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "headerLeads") as? WizHeaderView ?? WizHeaderView(reuseIdentifier: "headerLeads")
header.delegate = self
header.lblLeadSummary.text = sections[section].name
header.btnArrowButton.setImage(sections[section].icon, for: UIControlState.normal)
header.setCollapsed(sections[section].collapsed)
header.section = section
return header
}
func toggleSection(_ header: WizHeaderView, section: Int) {
let collapsed = !sections[section].collapsed
// Toggle collapse
sections[section].collapsed = collapsed
header.setCollapsed(collapsed)
// Adjust the height of the rows inside the section
tblLeadDetail?.beginUpdates()
for i in 0 ..< sections[section].options.count {
tblLeadDetail?.reloadRows(at: [IndexPath(row: i, section: section)], with: .automatic)
}
tblLeadDetail?.endUpdates()
}
#IBAction func placeOrder(_ sender: Any) {
let placeOrder = MainStoryBoard.instantiateViewController(withIdentifier: "PlaceOrder")
navigationController?.pushViewController(placeOrder, animated: true)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 35.0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10.0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return sections[(indexPath as NSIndexPath).section].collapsed! ? 0 : 70.0
}
func cellTextField(_with TextField: IQDropDownTextField, didSelectItem item: String?) {
switch TextField.tag {
case TextFieldTags.VisitType.rawValue:
visitType = item
break
case TextFieldTags.Industry.rawValue:
industry = item
break
case TextFieldTags.Source.rawValue:
source = item
break
case TextFieldTags.Status.rawValue:
status = item
break
case TextFieldTags.Country.rawValue:
country = item
break
case TextFieldTags.States.rawValue:
statesVal = item
break
default: break
}
}
func cellTextFieldDidEndEditing(_with textField: UITextField) {
switch textField.tag {
case TextFieldTags.ProspectName.rawValue:
prospectName = textField.text
break
case TextFieldTags.DissSummary.rawValue:
dissSummary = textField.text
break
case TextFieldTags.Requirements.rawValue:
requirements = textField.text
break
case TextFieldTags.AnticipatedDealValue.rawValue:
anticipatedDealValue = textField.text
break
case TextFieldTags.Probability.rawValue:
probability = textField.text
break
case TextFieldTags.NextStep.rawValue:
nextStep = textField.text
break
case TextFieldTags.Emp.rawValue:
emp = textField.text
break
case TextFieldTags.Rev.rawValue:
rev = textField.text
break
case TextFieldTags.AddressLine1.rawValue:
addressLine1 = textField.text
break
case TextFieldTags.AddressLine2.rawValue:
addressLine2 = textField.text
break
case TextFieldTags.City.rawValue:
cityVal = textField.text
break
case TextFieldTags.Mobile.rawValue:
mobile = textField.text
break
case TextFieldTags.Fax.rawValue:
fax = textField.text
break
case TextFieldTags.Zip.rawValue:
zip = textField.text
break
default: break
}
}
func stringFromDate(_ date:Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: date)
}
func cellTextFieldDate(_with textField: IQDropDownTextField, didSelect date: Date?) {
switch textField.tag {
case TextFieldTags.VisitDate.rawValue:
visitDate = stringFromDate(date!)
break
case TextFieldTags.FollowUpDate.rawValue:
followUpDate = stringFromDate(date!)
break
case TextFieldTags.ExpectedClosingDate.rawValue:
expectedClosingDate = stringFromDate(date!)
break
default: break
}
}
}
extension UIColor {
func getImage(size: CGSize) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image(actions: { rendererContext in
self.setFill()
rendererContext.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
})
}}
While using UITableView with reusable cell identifier, you have to store input of your UITextField or any other input fields when editing is finished into your data source which you are using to fill all cells. Because reusable cell manage memory itself to draw all your controls, so there are no guaranty about your data remains as it is when you scroll your tableview.
In this if - else expression
if dict["value"] as? String == "" {
cell.txtInput.setDate(Date(), animated: true)
} else{
cell.txtInput.selectedItem = dict["value"] as? String
}
you are setting two different UI elements depending on the dictionary value.
As cells are reused those UI states will remain until they are changed the next time.
You have to make sure that every UI element is set to a defined state to avoid unexpected behavior, so you need to set the other UI element, too. For example ([default value] is just a placeholder).
if dict["value"] as? String == "" {
cell.txtInput.setDate(Date(), animated: true)
cell.txtInput.selectedItem = [default value]
} else{
cell.txtInput.setDate([default value], animated: [default value])
cell.txtInput.selectedItem = dict["value"] as? String
}
If there is no unique default value you have to add properties to the data source to keep the current states of the date and the selected item.
Regarding the text field you have to update the data source after changing the text because in cellForRow the text field is always updated from the data source.
And - as always - don't use NS(Mutable)Array and NS(Mutable)Dictionary in Swift. You throw away the important type information and the mutable collection types are not related to the Swift native types. Finally you can delete all break lines in the switch expressions (except after default). They are redundant in Swift.
Related
Button in the table cell not working upon tapping
I am using below code for the UITableView, in every cell there are two buttons configured to perform certain actions as shown in the code. Earlier it was working fine, but now As I update this code to new version of Xcode it is not working, whenever I tap on the cell or the button in the cell it doesn't perform any action, neither it shows any error, but it just darkens the half of the cell with grey colour? Xcode earlier was 10 and now 11, swift 5 version same in both cases There is one fixed cell at the top and then there is list of cells as per the number of documents in the database What could be the issue? for information I am using Swift IOS and cloud firestore database class HomeViewController: UITableViewController { var posts = [Post]() var db: Firestore! var scores1 = [Scores]() var postAuthorId:String = "" var postAuthorname:String = "" var CommentAuthorName:String = "" var PostTitle:String = "" var postAuthorGender:String = "" var postAuthorEmail:String = "" var postAuthorfullname:String = "" var postAuthorSpinnerC:String = "" var postContent:String = "" var postCategory:String = "" var postAuthorPicUrl:String = "" var postTimeStamp:String = "" var l11:Int = 0 var postKey:String = "" private var documents: [DocumentSnapshot] = [] override func viewDidLoad() { super.viewDidLoad() db = Firestore.firestore() tableView.dataSource = self tableView.delegate = self retrieveAllPosts() getuserscores() var AViewController: UIViewController = UIViewController() var MyTabBarItem: UITabBarItem = UITabBarItem(title: nil, image: UIImage(named: "pencil")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal), selectedImage: UIImage(named: "pencil")) AViewController.tabBarItem = MyTabBarItem // Do any additional setup after loading the view. // navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleLogout)) } func getuserscores(){ let userRef = Firestore.firestore().collection("users").document(Auth.auth().currentUser!.uid) userRef.getDocument{(document, error) in if let document = document, document.exists{ let dataDescription = document.data().map(String.init(describing:)) ?? "nil" let l1 = document.get("l1") as! Int let l2 = document.get("l2") as! Int let l3 = document.get("l3") as! Int let l4 = document.get("l4") as! Int let newScores = Scores(_l1: l1, _l2: l2, _l3: l3, _l4: l4) self.scores1.append(newScores) } self.tableView.reloadData() } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func retrieveAllPosts(){ let postsRef = Firestore.firestore().collection("posts").order(by: "timestamp", descending: true).limit(to: 50) postsRef.getDocuments { (snapshot, error) in if let error = error { print(error.localizedDescription) } else { if let snapshot = snapshot { for document in snapshot.documents { let data = document.data() let username = data["post_author_username"] as? String ?? "" let postTitle = data["postTitle"] as? String ?? "" let postcategory = data["postcategory"] as? String ?? "" let postContent = data["postContent"] as? String ?? "" let postAuthorProfilePicUrl = data["post_user_profile_pic_url"] as? String ?? "" let postAuthorSpinnerC = data["post_author_spinnerC"] as? String let newSourse = Post(_documentId: document.documentID, _username: username, _postTitle: postTitle, _postcategory: postcategory, _postContent: postContent, _postuserprofileImagUrl: postAuthorProfilePicUrl, _postAuthorSpinncerC: postAuthorSpinnerC) self.posts.append(newSourse) } self.tableView.reloadData() } } } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tableView.estimatedRowHeight = 200 tableView.rowHeight = UITableView.automaticDimension } override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return scores1.count + posts.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.row == 0 && scores1.count == 1 { let cell = tableView.dequeueReusableCell(withIdentifier: "SmileMiles") as! ScoresCellInHomeScreen cell.set(scores: scores1[indexPath.row]) return cell } else if posts.count > (indexPath.row - 1 ) { let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell cell.btnComment.tag = indexPath.row - 1 cell.btnComment.addTarget(self, action: #selector(toComments(_:)), for: .touchUpInside) cell.favoritebutton.tag = indexPath.row - 1 cell.favoritebutton.addTarget(self, action: #selector(favupdate(_:)), for: .touchUpInside) cell.set(post: posts[indexPath.row - 1 ]) return cell } else { return UITableViewCell() } } #objc func favupdate(_ sender: AnyObject) { let commentbutton = sender as! UIButton let post = posts[commentbutton.tag] postKey = post._documentId // or what key value it is let userMarkRef = Firestore.firestore().collection("users").document(Auth.auth().currentUser!.uid).collection("marked_posts").document(postKey) let postRef = Firestore.firestore().collection("posts").document(postKey) postRef.getDocument{(document, error) in if let document = document, document.exists{ let dataDescription = document.data().map(String.init(describing:)) ?? "nil" self.postAuthorId = document.get("post_author_id") as! String self.postAuthorname = document.get("post_author_username") as! String self.PostTitle = document.get("postTitle") as! String self.postContent = document.get("postContent") as! String self.postAuthorEmail = document.get("post_author_email") as! String self.postCategory = document.get("postcategory") as! String self.postAuthorfullname = document.get("post_author_fullname") as! String self.postAuthorGender = document.get("post_author_gender") as! String self.postAuthorPicUrl = document.get("post_user_profile_pic_url") as! String // let l11:Bool = document.get("l1") as! Bool // self.postTimeStamp = document.get("post_timeStamp") as! String self.postAuthorSpinnerC = document.get("post_author_spinnerC") as! String } let postObject = [ "post_author_gender": self.postAuthorGender, // "post_author_dateOfBirth": self.dateOfBirth, "post_author_spinnerC": self.postAuthorSpinnerC, "post_author_fullname": self.postAuthorfullname, "post_author_id": self.postAuthorId, "post_author_username": self.postAuthorname, "post_author_email": self.postAuthorEmail, "postTitle": self.PostTitle, "postcategory": self.postCategory, "postContent": self.postContent, // "post_timestamp": FieldValue.serverTimestamp(), "post_user_profile_pic_url":self.postAuthorPicUrl, "post_id": self.postKey ] as [String : Any] userMarkRef.setData(postObject, merge: true) { (err) in if let err = err { print(err.localizedDescription) } print("Successfully set new user data") } } } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if indexPath.row == 0 { return 150 } else{ return UITableView.automaticDimension } } #objc func toComments(_ sender: AnyObject) { let commentbutton = sender as! UIButton let post = posts[commentbutton.tag] postKey = post._documentId // or what key value it is print(postKey + "hello") performSegue(withIdentifier: "toCommentsList", sender: self) } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if(segue.identifier == "toCommentsList"){ var vc = segue.destination as! CommentListViewController vc.postId = postKey } } }
Do this in tableview cellForRowAt function before return cell: cell.selectionStyle = .none return cell this will fix the issue
how to make checkmark to be selected depending on the array in swift 3?
I am having array in which selected name will be stored and passed to before view controller and when ever i need to go previous view controller then the previously selected check mark needs to be selected but here it is enabling the last selected element only the problem is if i select three then it is not selecting three it is check marking only the last element but i need the three selected can anyone help me how to make the check mark to be selected for three elements ? protocol ArrayToPass: class { func selectedArrayToPass(selectedStrings: [String]) } class FilterSelectionViewController: UIViewController,UITableViewDataSource,UITableViewDelegate { var productName = [String]() var productprice = [String]() var imageArray = [String]() var idArray = [Int]() let urlString = "http://www.json-generator.com/api/json/get/bOYOrkIOSq?indent=2" var values = [String]() var selected: Bool? var delegate: ArrayToPass? var nameSelection: Bool? var namesArray = [String]() override func viewDidLoad() { super.viewDidLoad() self.downloadJsonWithURL() tableDetails.separatorInset = UIEdgeInsets.zero activityIndicator.startAnimating() tableDetails.isHidden = true tableDetails.dataSource = self tableDetails.delegate = self let rightBarButton = UIBarButtonItem(title: "Apply", style: UIBarButtonItemStyle.plain, target: self, action: #selector(applyBarButtonActionTapped(_:))) self.navigationItem.rightBarButtonItem = rightBarButton tableDetails.estimatedRowHeight = UITableViewAutomaticDimension tableDetails.rowHeight = 60 // Do any additional setup after loading the view. } func applyBarButtonActionTapped(_ sender:UIBarButtonItem!){ self.delegate?.selectedArrayToPass(selectedStrings: values) navigationController?.popViewController(animated: true) } func downloadJsonWithURL() { let url = NSURL(string: urlString) URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSArray { for item in jsonObj! { if let itemDict = item as? NSDictionary{ if let name = itemDict.value(forKey: "name") { self.productName.append(name as! String) } if let price = itemDict.value(forKey: "value") { self.productprice.append(price as! String) } if let image = itemDict.value(forKey: "img") { self.imageArray.append(image as! String) } if let id = itemDict.value(forKey: "id") { self.idArray.append(id as! Int) } } } OperationQueue.main.addOperation({ self.tableDetails.reloadData() }) } }).resume() } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return productName.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "filterSelectionCell", for: indexPath) as! FilterSelectionCell activityIndicator.stopAnimating() activityIndicator.hidesWhenStopped = true tableDetails.isHidden = false cell.brandProductName.text = productName[indexPath.row] if nameSelection == true{ if namesArray.count != 0 { print(namesArray) for name in namesArray{ if productName[indexPath.row].contains(name){ print(productName[indexPath.row]) cell.accessoryType = .checkmark } else { cell.accessoryType = .none } } } } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){ selected = false if let cell = tableView.cellForRow(at: indexPath as IndexPath) { if cell.accessoryType == .checkmark{ cell.accessoryType = .none print("\(productName[indexPath.row])") values = values.filter{$0 != "\(productName[indexPath.row])"} selected = true } else{ cell.accessoryType = .checkmark } } if selected == true{ print(values) } else{ getAllTextFromTableView() } print(values) } func getAllTextFromTableView() { guard let indexPaths = self.tableDetails.indexPathsForSelectedRows else { // if no selected cells just return return } for indexPath in indexPaths { values.append(productName[indexPath.row]) } } here is the image for this
Basically do not manipulate the view (the cell). Use a data model. struct Product { let name : String let value : String let img : String let id : Int var selected = false init(dict : [String:Any]) { self.name = dict["name"] as? String ?? "" self.value = dict["value"] as? String ?? "" self.img = dict["img"] as? String ?? "" self.id = dict["id"] as? Int ?? 0 } } And never use multiple arrays as data source . That's a very bad habit. Declare the data source array as var products = [Product]() Parse the JSON data and do a (better) error handling func downloadJsonWithURL() { let url = URL(string: urlString)! let task = URLSession.shared.dataTask(with: url) { (data, response, error) in if error != nil { print(error!); return } do { if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { self.products = jsonObj.map{ Product(dict: $0) } DispatchQueue.main.async { self.tableDetails.reloadData() } } } catch { print(error) } } task.resume() } in cellForRow... assign the name to the label and set the checkmark depending on selected func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "filterSelectionCell", for: indexPath) let product = products[indexPath.row] cell.textLabel!.text = product.name cell.accessoryType = product.selected ? .checkmark : .none return cell } In didSelect... toggle selected and reload the row func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selected = products[indexPath.row].selected products[indexPath.row].selected = !selected tableView.reloadRows(at: [indexPath], with: .none) } To get all selected items is very easy, too. let selectedItems = products.filter{ $0.selected } or get only the names let selectedNames = products.filter{ $0.selected }.map{ $0.name } There is no need at all to get any information from the view. The controller gets the information always from the model and uses tableview data source and delegate to update the view. If you want to pass data to another view controller pass Product instances. They contain all relevant information.
how to pass multiple array data into a table view?
here i am having two arrays in the url and i need to display as shown in image below and i don't know any idea how to implement this in table view as shown in image { "Flat": [ { "price": "$5.00", "id": 11, "name": "Fixed" } ], "United Parcel Service": [ { "price": "$109.12", "id": 1, "name": "worldwide Expedited" }, { "price": "$120.18", "id": 2, "name": "worldwide Express saver" } ] } class shippingObject: NSObject { var id :NSInteger! var name :String! var price : String! } class CheckoutViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { #IBOutlet weak var scrollView: UIScrollView! #IBOutlet weak var titleLabel: UILabel! #IBOutlet weak var ContinueButton: UIButton! #IBOutlet weak var newAddress: UIButton! #IBOutlet var tableDetails: UITableView! #IBOutlet weak var shippingTableView: UITableView! #IBOutlet weak var HeightConstraint: NSLayoutConstraint! let url = "http://www.json-generator.com/api/json/get/bMlqRPbjGW?indent=2" let urlString = "http://www.json-generator.com/api/json/get/bVWKKHtWbm?indent=2" var detailsArray : [shippingObject] = [] var shippingArray :[[String: AnyObject]] = [] var price = [String]() var keys = [String]() var name = [String]() var methodArray : [[String: AnyObject]] = [] let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray) var arrayss = [String:Any]() var checkIsRadioSelect = [Int]() var checkIsButtonEnable = [Int]() var checkIsPaymentRadioSelect = [Int]() let response : [Int] = [] override func viewDidLoad() { super.viewDidLoad() tableDetails.isHidden = true shippingTableView.isHidden = true scrollView.isScrollEnabled = false titleLabel.isHidden = true newAddress.layer.cornerRadius = 5 ContinueButton.layer.cornerRadius = 5 myActivityIndicator.frame = CGRect(x: 130, y: 320, width: 30, height: 30) myActivityIndicator.hidesWhenStopped = true myActivityIndicator.startAnimating() view.addSubview(myActivityIndicator) myActivityIndicator.translatesAutoresizingMaskIntoConstraints = false let horizontalConstraint = NSLayoutConstraint(item: myActivityIndicator, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0) view.addConstraint(horizontalConstraint) let verticalConstraint = NSLayoutConstraint(item: myActivityIndicator, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0) view.addConstraint(verticalConstraint) self.shippingmethodURL() self.shippingaddressURL() tableDetails.delegate = self tableDetails.dataSource = self tableDetails.rowHeight = UITableViewAutomaticDimension tableDetails.estimatedRowHeight = 300 shippingTableView.delegate = self shippingTableView.dataSource = self shippingTableView.rowHeight = UITableViewAutomaticDimension shippingTableView.estimatedRowHeight = 300 // Do any additional setup after loading the view. } func shippingaddressURL() { let url = NSURL(string: self.url) URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary { self.shippingArray = (jsonObj!.value(forKey: "address") as? [[String: AnyObject]])! OperationQueue.main.addOperation({ self.tableDetails.reloadData() }) } }).resume() } func assignValuesToObjecs(arrayvalues: NSArray){ (arrayvalues as NSArray).enumerateObjects({ (object, count, stop) in let Object :shippingObject = shippingObject() Object.name = (object as AnyObject) .value(forKey: "name") as! String Object.id = (object as AnyObject) .value(forKey: "id") as! NSInteger Object.price = (object as AnyObject).value(forKey: "price")as! String self.detailsArray.append(Object) }) } func shippingmethodURL() { let url = NSURL(string: self.urlString) URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary { print(jsonObj!) self.arrayss = jsonObj as! [String : Any] print(self.arrayss) //self.shippingArray = (jsonObj!.value(forKey: "Flat") as? [[String: AnyObject]])! self.assignValuesToObjecs(arrayvalues :(jsonObj?["Flat"] as? [[String: AnyObject]]! as? NSArray)!) self.assignValuesToObjecs(arrayvalues :(jsonObj?["United Parcel Service"] as? [[String: AnyObject]]! as? NSArray)!) self.methodArray = (jsonObj!.value(forKey: "United Parcel Service") as? [[String: AnyObject]])! OperationQueue.main.addOperation({ self.keys = ["Flat","United Parcel Service"] self.shippingTableView.reloadData() }) } }).resume() } #IBAction func selectRadioButton(_ sender: KGRadioButton) { let chekIndex = self.checkIsRadioSelect.index(of: sender.tag) _ = self.checkIsButtonEnable.index(of: sender.tag) if sender.isSelected { shippingTableView.isHidden = false scrollView.isScrollEnabled = true titleLabel.isHidden = false } else{ if(chekIndex == nil){ self.checkIsRadioSelect.removeAll(keepingCapacity: false) self.checkIsRadioSelect.append(sender.tag) self.checkIsButtonEnable.removeAll(keepingCapacity: false) self.checkIsButtonEnable.append(sender.tag) self.tableDetails.reloadData() } } } func numberOfSections(in tableView: UITableView) -> Int{ var count = 0 if tableView.tag == 100{ return 1 } else if tableView.tag == 110 { return arrayss.count } return count } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if tableView.tag == 100{ return "SHIPPING ADDRESS" } else { return self.keys[section] } } func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int){ if tableView.tag == 100 { let header = view as! UITableViewHeaderFooterView header.textLabel?.textColor = UIColor.gray header.textLabel?.textAlignment = NSTextAlignment.center header.textLabel?.font = UIFont(name: "Futura", size: 17) } else { let header = view as! UITableViewHeaderFooterView header.tintColor = UIColor.white header.textLabel?.textColor = UIColor.darkGray header.textLabel?.textAlignment = NSTextAlignment.left header.textLabel?.font = UIFont(name: "Futura", size: 17) } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{ if tableView.tag == 100{ return shippingArray.count } else { var key = self.keys[section] print(key) var a :[Any] = arrayss[key] as! [Any] return a.count } } #IBAction func continueButtonAction(_ sender: Any) { let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil) let nextViewController = storyBoard.instantiateViewController(withIdentifier: "payment") as! paymentMethodViewController self.present(nextViewController, animated:true, completion:nil) } #IBAction func deleteAction(_ sender: UIButton) { shippingArray.remove(at:sender.tag) self.tableDetails.reloadData() } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if tableView.tag == 100{ let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! addressTableViewCell tableDetails.isHidden = false myActivityIndicator.stopAnimating() let arr = shippingArray[indexPath.row] cell.deleteButton.tag = indexPath.row cell.nameLabel.text = arr["name"] as? String cell.addressLabel.text = arr["address"]as? String let mobilenumber : Int = arr["number"] as! Int cell.mobileNumberLabel.text = String(describing: mobilenumber) cell.radioButton.tag = indexPath.row cell.editButton.tag = indexPath.row cell.deleteButton.tag = indexPath.row cell.editButton.isHidden = true cell.deleteButton.isHidden = true let checkIndex = self.checkIsRadioSelect.index(of: indexPath.row) if(checkIndex != nil){ shippingTableView.isHidden = false scrollView.isScrollEnabled = true titleLabel.isHidden = false cell.radioButton.isSelected = true cell.editButton.isHidden = false cell.deleteButton.isHidden = false }else{ cell.radioButton.isSelected = false cell.editButton.isHidden = true cell.deleteButton.isHidden = true } return cell } else { let cell = tableView.dequeueReusableCell(withIdentifier: "shippingCell", for: indexPath) as! shippingMethodTableViewCell // let arr = detailsArray[indexPath.row] // cell.methodNameLabel.text = arr.name // cell.priceLabel.text = arr.price var key = self.keys[indexPath.section] print(key) var a :[Any] = arrayss[key] as! [Any] var dictionary = a[indexPath.row]; cell.methodNameLabel.text = dictionary ["name"] + dictionary ["price"] return cell } } any help would be of great appreciation
Sectional UITableView can rescue you from such situation. I have assumed arrayss as dictionary and jsonObj. // number of rows in table view func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { var key= self.keys[section] print(key) var a :[Any] = arrayss[key] as! [Any] return a.count } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 2 // if not fixed self.keys.count or self.arrayss.count } // create a cell for each table view row func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // create a new cell if needed or reuse an old one let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell! var key= self.keys[indexPath.section] print(key) var a :[Any] = arrayss[key] as! [Any] var dictionary = a[indexPath.row] as! [String:Any]; cell.textLabel.text = dictionary ["name"]+ dictionary ["price"] // you can set the text from the data model return cell } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return self.keys[section] } ---- EDIT---- In order to get all keys and values of dictionary from a dictionary separately. var key = Array(arrayss.keys).sorted(by: >) var values = Array(arrayss.values).sorted(by: >) Hope it helps. Happy Coding!!
Check For Empty Array From JSON
I am trying to get an array of strings from JSON and I'm trying to figure out how to deal with it if the returned array is empty. In some cases, the returned value is [] and for other cases, the array contains string values. It is crashing because of unexpectedly finding a nil value. For clarification, the gyms array is passed from another class and everything here works without the code for the images. Here is my relevant code: var gyms = [AnyObject]() var imageArrays = [[String]?]() In viewDidLoad() getGymImages() Methods to get JSON data: func getGymImages() { var index = 0 for dictionary in gyms { let id = dictionary["id"] as! String let urlString = String("https://gyminyapp.azurewebsites.net/api/GymImage/\(id)") let url = NSURL(string: urlString) let data = NSData(contentsOfURL: url!) do { let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) for imageArray in json as! [AnyObject] { imageArrays.append((imageArray as? [String])!) } } catch { print("Error") } index += 1 } addImagesToGyms() } func addImagesToGyms() { var index = 0; for array in imageArrays { var dictionary = gyms[index] as! [String:AnyObject] dictionary["images"] = array gyms[index] = dictionary index += 1 } } In cellForRowAtIndexPath() let gymImages = dictionary["images"] as! [String] if gymImages.count > 0 { let firstImageURL = gymImages[0] as String cell.cellImageView.sd_setImageWithURL(NSURL(string: firstImageURL)) } EDIT: I was asked to show more of the file, so here it is. import UIKit class GymListTableViewController: UITableViewController { var gyms = [AnyObject]() var gymName: String? var gymAddress: String? var gymPhoneNumber: String? var gymWebsite: String? var gymID: String? var gymLatitude: String? var gymLongitude: String? var maxDistance: Double? var myLocation: CLLocation? var milesArray = [Double]() var imageArrays = [[String]?]() var segmentedControl: UISegmentedControl? override func viewDidLoad() { super.viewDidLoad() self.title = "Gyms" tableView.registerNib(UINib(nibName: "GymListTableViewCell", bundle: nil), forCellReuseIdentifier: "gymCell") self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "waypoint_map"), style: .Done, target: self, action: #selector(showMapView)) self.navigationItem.rightBarButtonItem?.tintColor = BarItems.greenTintColor segmentedControl = UISegmentedControl(items: ["A-Z", "Z-A", "Rating", "Distance"]) segmentedControl?.sizeToFit() segmentedControl?.selectedSegmentIndex = 0 segmentedControl!.setTitleTextAttributes([NSFontAttributeName: UIFont(name:"Helvetica-Light", size: 15)!], forState: UIControlState.Normal) segmentedControl?.addTarget(self, action: #selector(changeSelectedSegmentIndex), forControlEvents: .ValueChanged) self.navigationItem.titleView = segmentedControl sortAlphabetically() let backgroundImage = UIImage(named: "gray_background") let backgroundImageView = UIImageView(image: backgroundImage) tableView.backgroundView = backgroundImageView addDistancesToGyms() getGymImages() // geocodeAddresses() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func addDistancesToGyms() { var index = 0 for distance in milesArray { var dictionary = gyms[index] as! [String:AnyObject] dictionary["distance"] = distance gyms[index] = dictionary index += 1 } } func getGymImages() { var index = 0 for dictionary in gyms { let id = dictionary["id"] as! String let urlString = String("https://gyminyapp.azurewebsites.net/api/GymImage/\(id)") let url = NSURL(string: urlString) let data = NSData(contentsOfURL: url!) do { let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) for imageArray in json as! [AnyObject] { imageArrays.append((imageArray as? [String])!) } } catch { print("Error") } index += 1 } addImagesToGyms() } func addImagesToGyms() { var index = 0; for array in imageArrays { var dictionary = gyms[index] as! [String:AnyObject] dictionary["images"] = array gyms[index] = dictionary index += 1 } } func changeSelectedSegmentIndex() { let segmentTouched = segmentedControl?.selectedSegmentIndex if segmentTouched == 0 { sortAlphabetically() } else if segmentTouched == 1 { sortReverseAlphabetically() } else if segmentTouched == 2 { sortByRatingAscending() } else { sortByDistanceAscending() } } func sortAlphabetically() { gyms.sortInPlace{ (($0 as! Dictionary<String, AnyObject>)["name"] as? String) < (($1 as! Dictionary<String, AnyObject>)["name"] as? String) } tableView.reloadData() } func sortReverseAlphabetically() { gyms.sortInPlace{ (($0 as! Dictionary<String, AnyObject>)["name"] as? String) > (($1 as! Dictionary<String, AnyObject>)["name"] as? String) } tableView.reloadData() } func sortByRatingAscending() { // TODO: Sort by rating } func sortByDistanceAscending() { gyms.sortInPlace{ (($0 as! Dictionary<String, AnyObject>)["distance"] as? Double) < (($1 as! Dictionary<String, AnyObject>)["distance"] as? Double) } tableView.reloadData() } func showMapView() { self.performSegueWithIdentifier("displayMapSegue", sender: self.navigationController) } // MARK: - Table view data source override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return gyms.count } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 131 } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("gymCell", forIndexPath: indexPath) as! GymListTableViewCell let dictionary = gyms[indexPath.row] let addressDictionary = dictionary["address"] let street = addressDictionary!!["streetAddress"] as! String let city = addressDictionary!!["city"] as! String let state = addressDictionary!!["state"] as! String let zipInt = addressDictionary!!["zipCode"] as! Int let zipCode = String(zipInt) let addressString = String("\(street) " + "\(city), " + "\(state) " + "\(zipCode)") cell.backgroundColor = UIColor.clearColor() cell.gymNameLabel.text = dictionary["name"] as? String cell.gymAddressLabel.text = addressString let miles = dictionary["distance"] as! Double let milesString = String(format: "%.1f miles", miles) let milesLabelString = milesString cell.milesLabel.text = milesLabelString let gymImages = dictionary["images"] as! [String] if gymImages.count > 0 { let firstImageURL = gymImages[0] as String cell.cellImageView.sd_setImageWithURL(NSURL(string: firstImageURL)) } return cell } override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let dictionary = gyms[indexPath.row] if dictionary["name"] as? String != nil { self.gymName = dictionary["name"] as? String } let cell = tableView.cellForRowAtIndexPath(indexPath) as! GymListTableViewCell self.gymAddress = cell.gymAddressLabel.text if dictionary["phone"] as? String != nil { self.gymPhoneNumber = dictionary["phone"] as? String } if dictionary["website"] as? String != nil { self.gymWebsite = dictionary["website"] as? String } if dictionary["id"] as? String != nil { self.gymID = dictionary["id"] as? String } if dictionary["latitude"] as? String != nil { self.gymLatitude = dictionary["latitude"] as? String } if dictionary["longitude"] as? String != nil { self.gymLongitude = dictionary["longitude"] as? String } self.performSegueWithIdentifier("detailFromListSegue", sender: self.navigationController) } // MARK: - Navigation override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "detailFromListSegue" { let gymDetailVC = segue.destinationViewController as! DetailTableViewController if self.gymName != nil { gymDetailVC.gymName = self.gymName } else { gymDetailVC.gymName = nil } if self.gymAddress != nil { gymDetailVC.gymAddress = self.gymAddress } else { gymDetailVC.gymAddress = nil } if self.gymPhoneNumber != nil { gymDetailVC.gymPhoneNumber = self.gymPhoneNumber } else { gymDetailVC.gymPhoneNumber = nil } if self.gymWebsite != nil { gymDetailVC.gymWebsite = self.gymWebsite } else { gymDetailVC.gymWebsite = nil } if self.gymID != nil { gymDetailVC.gymID = self.gymID! } else { // gymDetailVC.gymID = nil } if self.gymLatitude != nil { gymDetailVC.gymLatitude = self.gymLatitude! } if self.gymLongitude != nil { gymDetailVC.gymLongitude = self.gymLongitude! } } } }
numberOfRowsInSection should be returning gymImages.count if it isn't already. Then as a safeguard you can always do if indexPath.row < gymImages.count { } before accessing the content in cellForRowAtIndexPath
Get cells from last section uitableview
I have dynamically tableview with many section whitch is adding in run time by tap on button. This button must send data from cells in last section (many type of custom cells). How can i get cells or indexpath's of cells in last section? I was trying to add these cells to array in cellForRowAtIndexPath, but i use dequeueReusableCellWithIdentifier to create this cells, so this is problematic. Edit: My code: class ProcessViewController: UITableViewController, SWRevealViewControllerDelegate{ //this will be cell struct field { var name, type, id, hint, output, specialType:String var selectOptions:NSMutableArray var settings:NSArray } struct group { var name:String var fields:[field] } // this will be sections struct block { var groups:[group] var id, name:String } var blocks:[block] = [] //fields in last (active) block //var fields:[field] = [] //array of cells with some input var cellsInTable:NSMutableArray = [] var cellWithCustomHeight:[NSIndexPath] = [] #IBOutlet var sendFormButton: UIButton! var jsonData:NSDictionary? #IBOutlet var menuButton: UIBarButtonItem! var allfields:[field] = [] var numberOfOldCells = 0 override func viewDidLoad() { super.viewDidLoad() navigationController?.navigationBar.barTintColor = UIColor(red: 62/255.0, green: 80/255.0, blue: 178/255.0, alpha: 1); //print("json w srodku \(self.jsonData)") if self.revealViewController() != nil { self.revealViewController().delegate = self menuButton.target = self.revealViewController() menuButton.action = "revealToggle:" self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer()) } //print(self.jsonData) let data = self.jsonData?.valueForKey("data") as! NSMutableDictionary //set title of the screen self.title = data.valueForKey("name") as? String //hide button if process is completed if(data.valueForKey("progress") as! Int == 1){ sendFormButton.hidden = true //print("asdasd") } parseData(data) //print("Bloki: \(self.blocks)") } func parseData(data:NSMutableDictionary){ self.cellsInTable = [] //blocks is the sections in tableView, fields is a cells and groups (for now) let blocks = data.valueForKey("blocks") as! NSMutableArray self.blocks = [] self.allfields = [] blocks.enumerateObjectsUsingBlock {obj, i, stop in //print( obj.valueForKey("input")) var inputs:NSMutableArray = [] //get inputs from current block, if input is only one it is dictionary in swift if let kinputs = obj.valueForKey("input") as? NSArray{ inputs = NSMutableArray(array:kinputs) }else{ inputs.addObject(obj.valueForKey("input")!) } inputs.removeLastObject() var outputs:NSArray = [] if let koutputs = obj.valueForKey("output") as? NSArray{ outputs = koutputs } //print("wrona \(outputs) kracze razy: \(outputs.count)") var tmpGroups:[group] = [] //inupt is a group inputs.enumerateObjectsUsingBlock({input, j, stop in //print("input w petli: \(input)") if(input.valueForKey("fields") != nil){ let forms = NSMutableArray(array:input.valueForKey("fields") as! NSArray) var tmpFields:[field] = [] let groupField:field = field(name: input.valueForKey("name") as! String, type: "group", id: "0", hint: input.valueForKey("name") as! String, output: "", specialType: "group", selectOptions: [], settings:[] ) tmpFields.append(groupField) forms.enumerateObjectsUsingBlock({fieldObj, k, stop in let fromId = fieldObj.valueForKey("id") as! String var output:String = "" let tmpOutputs = outputs.valueForKey(fromId) as! NSArray print(tmpOutputs) print(output) if tmpOutputs.count != 0{ if let _ = tmpOutputs[0] as? NSString{ output = tmpOutputs[0] as! String } } else { //print(outputs.valueForKey(String(k))) } let selectOptions = NSMutableArray(array: fieldObj.valueForKey("selectOptions") as! NSArray) //print("asdasdad\(fieldObj)") let tmpField:field = field( name: fieldObj.valueForKey("name") as! String, type: fieldObj.valueForKey("type") as! String, id: fromId, hint: fieldObj.valueForKey("hint") as! String, output:output, specialType: fieldObj.valueForKey("specialType") as! String, selectOptions: selectOptions, settings:fieldObj.valueForKey("settings") as! NSArray ) tmpFields.append(tmpField) }) let tmpGroup:group = group(name: input.valueForKey("name") as! String, fields: tmpFields) tmpGroups.append(tmpGroup) } }) let tmpBlock:block = block(groups: tmpGroups, id: obj.valueForKey("id") as! String, name: obj.valueForKey("name") as! String ) self.blocks.append(tmpBlock) } } func revealController(revealController: SWRevealViewController!, willMoveToPosition position: FrontViewPosition) { UIApplication.sharedApplication().sendAction("resignFirstResponder", to:nil, from:nil, forEvent:nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return self.blocks[section].name as String } override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return blocks.count } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { var formsInGroup:Int = 0; for group in self.blocks[section].groups{ formsInGroup += group.fields.count } return formsInGroup //return blocks[section].fields.count } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { if(self.cellWithCustomHeight.contains(indexPath)){ return CGFloat(132) } else { return CGFloat(47) } } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //geting fields in current group self.allfields = [] for group in self.blocks[indexPath.section].groups { self.allfields.appendContentsOf(group.fields) } print("bloki \(self.blocks.count), sekcje: \(indexPath.section)") //print(self.allfields.count) let cellType = self.allfields[indexPath.row].type as String //print("row: \(indexPath.row), sections: \(indexPath.section)") switch cellType { case "group": let cell = tableView.dequeueReusableCellWithIdentifier("groupCell", forIndexPath: indexPath) as! groupCell cell.groupName.text = self.allfields[indexPath.row].name return cell case "text": if(self.allfields[indexPath.row].specialType == "date"){ let cell = tableView.dequeueReusableCellWithIdentifier("datePickerCell", forIndexPath: indexPath) as! datePickerCell cell.hint.text = self.allfields[indexPath.row].name as String let output = self.allfields[indexPath.row].output as String cell.selectionStyle = UITableViewCellSelectionStyle.None self.cellWithCustomHeight.append(indexPath) if(output != ""){ print(self.allfields[indexPath.row].name) //print( " output: \(output)") let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "dd-mm-yyyy" let date = dateFormatter.dateFromString(self.allfields[indexPath.row].output) cell.date.setDate(date!, animated: false) cell.date.userInteractionEnabled = false } if !self.cellsInTable.containsObject(cell){ self.cellsInTable.addObject(cell) } setCellsToSend(indexPath.section) return cell }else{ let cell = tableView.dequeueReusableCellWithIdentifier("textCell", forIndexPath: indexPath) as! textCell cell.hint.text = self.allfields[indexPath.row].name as String let output = self.allfields[indexPath.row].output as String cell.settings = self.allfields[indexPath.row].settings if self.allfields[indexPath.row].specialType == "number" { cell.input.keyboardType = UIKeyboardType.NumberPad } cell.input.text = "" cell.selectionStyle = UITableViewCellSelectionStyle.None if(output != ""){ //print(fields[indexPath.row].name) //print(output) cell.input.text = self.allfields[indexPath.row].output cell.input.userInteractionEnabled = false } if !self.cellsInTable.containsObject(cell){ self.cellsInTable.addObject(cell) } else { print("kuuuuuurwa \(self.allfields[indexPath.row].name)") } setCellsToSend(indexPath.section) return cell } case "checkbox": let cell = tableView.dequeueReusableCellWithIdentifier("checkBoxCell", forIndexPath: indexPath) as! checkBoxCell cell.selections = self.allfields[indexPath.row].selectOptions cell.hint.text = self.allfields[indexPath.row].name as String cell.settings = self.allfields[indexPath.row].settings cell.indexPath = indexPath cell.selectionStyle = UITableViewCellSelectionStyle.None if !self.cellsInTable.containsObject(cell){ self.cellsInTable.addObject(cell) } else { print("kuuuuuurwa \(self.allfields[indexPath.row].name)") } setCellsToSend(indexPath.section) return cell default: let cell = tableView.dequeueReusableCellWithIdentifier("unknownCell", forIndexPath: indexPath) cell.textLabel?.text = "niezaimplementowany typ pola: \(cellType)" cell.selectionStyle = UITableViewCellSelectionStyle.None return cell } } func setCellsToSend(sectionInTable:Int){ if(sectionInTable == self.blocks.count-2){ print("sekcja \(sectionInTable)") self.numberOfOldCells = self.cellsInTable.count } } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if(segue.identifier == "showCheckBox"){ let checkBoxController = segue.destinationViewController as! CheckBoxController let cell = sender as! checkBoxCell //print(cell.settings) checkBoxController.selections = cell.selections checkBoxController.multiple = checkboxMultiValue(cell.settings) checkBoxController.indexPath = cell.indexPath checkBoxController.selected = cell.output } } func checkboxMultiValue(settings:NSArray)->Bool{ var boolToReturn:Bool = false settings.enumerateObjectsUsingBlock({obj, i, end in if(obj.valueForKey("name") as! String == "multi_values"){ if( obj.valueForKey("checked") as! String == "multi_values"){ boolToReturn = true; } } //print(i) }) return boolToReturn } #IBAction func saveSelectedSelections(segue: UIStoryboardSegue) { let checkBoxController = segue.sourceViewController as! CheckBoxController let selectedRowsPath = checkBoxController.tableView.indexPathsForSelectedRows var output:[String] = [] if(selectedRowsPath?.count > 0){ for rowPath in selectedRowsPath!{ let cell = checkBoxController.tableView.cellForRowAtIndexPath(rowPath) as! selectionCell output.append(cell.nameLabel.text! as String) } } let cell = self.tableView.cellForRowAtIndexPath(checkBoxController.indexPath!) as! checkBoxCell cell.output = output } #IBAction func sendForm(sender: AnyObject) { print("\(self.numberOfOldCells) i \(self.cellsInTable.count)") var postData:Dictionary<Int, AnyObject> = [:] var post = "" var iterator:Int = 0 for var i = self.numberOfOldCells; i < self.cellsInTable.count; ++i{ if let cellText = self.cellsInTable[i] as? textCell{ //print("cellText \(cellText.input.text as String!)") postData[iterator] = cellText.input.text as AnyObject! post += "\(iterator):{'\(cellText.input.text! as String)'}&" iterator++ } else if let cellCheckBox = self.cellsInTable[i] as? checkBoxCell{ //print(cellCheckBox.output) let checkBoxOutput = cellCheckBox.output var data:String = "" for var i = 0; i < checkBoxOutput.count; ++i { if ( i == 0) { data += checkBoxOutput[i] }else{ data += ";\(checkBoxOutput[i])" } } post += "\(iterator):{'\(data)'}&" postData[iterator] = data as AnyObject! //postData.append(data as String!) iterator++ }else if let cellDate = self.cellsInTable[i] as? datePickerCell{ //print(cellDate.date.date) let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "dd-mm-yyyy" let date = dateFormatter.stringFromDate(cellDate.date.date) postData[iterator] = date as AnyObject! post += "\(iterator):{'\(date)'}&" //postData.append(date as String!) iterator++ } } //print("Post data:\(postData)") //getNewBlock(NSKeyedArchiver.archivedDataWithRootObject(postData)) print(post) getNewBlock(post.dataUsingEncoding(NSASCIIStringEncoding)!) } func getNewBlock(postData:NSData){ //print("Post data jako nsdata:\(postData)") //var blockId = "a" let blockId = blocks.last!.id let url:NSURL = NSURL(string: "http://app.proces.io/Cloud/?Systems/Proces/Process/Block/makeAction/id-\(blockId)")! print(url) let postLength:NSString = String( postData.length ) let request:NSMutableURLRequest = NSMutableURLRequest(URL: url) request.HTTPMethod = "POST" request.HTTPBody = postData request.setValue(postLength as String, forHTTPHeaderField: "Content-Length") request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Accept") let session = NSURLSession.sharedSession() let task = session.dataTaskWithRequest(request) { urlData, response, error in var requestData: NSDictionary? do{ requestData = try NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers ) as? NSDictionary //print(requestData) if ( requestData != nil ) { let success:NSInteger = requestData!.valueForKey("success") as! NSInteger if(success == 1) { //print(self.blocks.count) let data = requestData!.valueForKey("process") as! NSMutableDictionary //print(self.allfields) //self.numberOfOldCells = self.cellsInTable.count //print(self.blocks) self.allfields = [] self.parseData(data) //print(self.blocks.count) dispatch_async(dispatch_get_main_queue(), { () -> Void in self.tableView.reloadData() }) } else { print("cos poszlo nie tak \(requestData?.valueForKey("validation_errors")), values: \(requestData?.valueForKey("values"))") } } } catch let error as NSError{ print("cos poszlo nie tak: \(error)") } } task.resume() } Edit 2: minimal version with some explains: class ProcessViewController: UITableViewController, SWRevealViewControllerDelegate{ //this will be cell struct field { var name, type, id, hint, output, specialType:String var selectOptions:NSMutableArray var settings:NSArray } struct group { var name:String var fields:[field] } // this will be sections struct block { var groups:[group] var id, name:String } var blocks:[block] = [] var jsonData:NSDictionary? #IBOutlet var menuButton: UIBarButtonItem! var allfields:[field] = [] var numberOfOldCells = 0 override func viewDidLoad() { super.viewDidLoad() parseData(data) } func parseData(data:NSMutableDictionary){ //in this function i have a parsing data from json, whitch create me array of blocks (global variable "blocks") } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return self.blocks[section].name as String } override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return blocks.count } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { var formsInGroup:Int = 0; for group in self.blocks[section].groups{ formsInGroup += group.fields.count } return formsInGroup //return blocks[section].fields.count } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { if(self.cellWithCustomHeight.contains(indexPath)){ return CGFloat(132) } else { return CGFloat(47) } } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //geting fields in current group self.allfields = [] for group in self.blocks[indexPath.section].groups { self.allfields.appendContentsOf(group.fields) } let cellType = self.allfields[indexPath.row].type as String switch cellType { case "group": let cell = tableView.dequeueReusableCellWithIdentifier("groupCell", forIndexPath: indexPath) as! groupCell cell.groupName.text = self.allfields[indexPath.row].name return cell case "text": if(self.allfields[indexPath.row].specialType == "date"){ let cell = tableView.dequeueReusableCellWithIdentifier("datePickerCell", forIndexPath: indexPath) as! datePickerCell cell.hint.text = self.allfields[indexPath.row].name as String let output = self.allfields[indexPath.row].output as String cell.selectionStyle = UITableViewCellSelectionStyle.None self.cellWithCustomHeight.append(indexPath) if(output != ""){ print(self.allfields[indexPath.row].name) //print( " output: \(output)") let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "dd-mm-yyyy" let date = dateFormatter.dateFromString(self.allfields[indexPath.row].output) cell.date.setDate(date!, animated: false) cell.date.userInteractionEnabled = false } return cell }else{ let cell = tableView.dequeueReusableCellWithIdentifier("textCell", forIndexPath: indexPath) as! textCell cell.hint.text = self.allfields[indexPath.row].name as String let output = self.allfields[indexPath.row].output as String cell.settings = self.allfields[indexPath.row].settings if self.allfields[indexPath.row].specialType == "number" { cell.input.keyboardType = UIKeyboardType.NumberPad } cell.input.text = "" cell.selectionStyle = UITableViewCellSelectionStyle.None if(output != ""){ cell.input.text = self.allfields[indexPath.row].output cell.input.userInteractionEnabled = false } return cell } case "checkbox": let cell = tableView.dequeueReusableCellWithIdentifier("checkBoxCell", forIndexPath: indexPath) as! checkBoxCell cell.selections = self.allfields[indexPath.row].selectOptions cell.hint.text = self.allfields[indexPath.row].name as String cell.settings = self.allfields[indexPath.row].settings cell.indexPath = indexPath cell.selectionStyle = UITableViewCellSelectionStyle.None return cell default: let cell = tableView.dequeueReusableCellWithIdentifier("unknownCell", forIndexPath: indexPath) cell.textLabel?.text = "niezaimplementowany typ pola: \(cellType)" cell.selectionStyle = UITableViewCellSelectionStyle.None return cell } } #IBAction func sendForm(sender: AnyObject) { // in this function i must send data from cells in last section and add new section on the basis of request }