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
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.
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.
when i keep break points at the point mentioned in image then the data was loading without crashing the application and when i didn't kept the break points and make the application to run then it was crashing can any one help me how to clear the error
my code is shown below
let url = "http://www.json-generator.com/api/json/get/bUKEESvnvS?indent=2"
var orderdetailsArray : [[String:AnyObject]] = []
var itemsArray : [[String:AnyObject]] = []
var orderid = [Any]()
var orderdate = [Any]()
var subTotal = [Int]()
var shippingPrice = [Int]()
var tax = [Int]()
var grandTotal = [Int]()
var shippingAddress = [AnyObject]()
var shippingMethod = [AnyObject]()
var billingAddress = [AnyObject]()
var paymentMethod = [AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithURL()
tableDetails.delegate = self
tableDetails.dataSource = self
tableDetails.estimatedRowHeight = 600
// Do any additional setup after loading the view.
}
func downloadJsonWithURL() {
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.orderdetailsArray = (jsonObj!.value(forKey: "Orders detail") as? [[String: AnyObject]])!
if let firstDictInfo = self.orderdetailsArray.first as? [String:Any] {
self.itemsArray = firstDictInfo["Items detail"] as! [[String : AnyObject]]
}
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
func numberOfSections(in tableView: UITableView) -> Int{
return 3
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if (section == 0){
return ""
}
else if (section == 1){
return ""
}
else{
return "Ordered Items"
}
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int){
let header = view as! UITableViewHeaderFooterView
header.textLabel?.textColor = UIColor.darkGray
header.textLabel?.textAlignment = NSTextAlignment.center
header.textLabel?.font = UIFont(name: "Futura", size: 17)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if (section == 0){
return 1
}else if (section == 1){
return 1
}
else{
return itemsArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "ordercell", for: indexPath) as! OrdersTableViewCell
cell.orderDateLabel.text = orderdetailsArray[0]["OrderDate"] as? String
cell.orderIdLabel.text = orderdetailsArray[0]["OrderId"]! as? String
let totalPrice : Int = orderdetailsArray[0]["Shipping"]! as! Int
let price = Float(totalPrice)
cell.shippingLabel.text = "$" + "\(price)"
let subTotalPrice : Int = orderdetailsArray[0]["SubTotal"]! as! Int
let subtotalprice = Float(subTotalPrice)
cell.subTotalLabel.text = "$" + "\(subtotalprice)"
let taxPrice : Int = orderdetailsArray[0]["Tax"]! as! Int
let taxPriceFloat = Float(taxPrice)
cell.taxLabel.text = "$" + "\(taxPriceFloat)"
let grandTotal : Int = self.orderdetailsArray[0]["GrandTotal"]! as! Int
let grandPriceFloat = Float(grandTotal)
cell.grandTotalLabel.text = "$" + "\(grandPriceFloat)"
return cell
}
else if (indexPath.section == 1){
let cell = tableView.dequeueReusableCell(withIdentifier: "shippingcell", for: indexPath) as! ShippingTableViewCell
cell.shippingMethodLabel.text = orderdetailsArray[0]["ShippingMethod"] as? String
cell.shippingAddressLabel.text = orderdetailsArray[0]["ShippingAddress"]! as? String
cell.billingAddressLabel.text = orderdetailsArray[0]["BillingAddress"]! as? String
cell.paymentMethodLabel.text = orderdetailsArray[0]["PayMentMethod"]! as? String
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "orderdetailscell", for: indexPath) as! OrderDetailsTableViewCell
let array = itemsArray[indexPath.row]
let price : Int = array["ItemPrice"] as! Int
let grandPriceFloat = Float(price)
cell.priceLabel.text = "$" + "\(grandPriceFloat)"
let quant : Int = array["ItemQty"] as! Int
cell.quantityLabel.text = "\(quant)"
cell.productNameLabel.text = array["ItemName"] as? String
let subTotal : Int = array["ItemSubTotal"] as! Int
let subPriceFloat = Float(subTotal)
cell.subTotalLabel.text = "$" + "\(subPriceFloat)"
let grandTotal : Int = array["ItemSku"] as! Int
cell.skuLabel.text = "\(grandTotal)"
return cell
}
}
You need to confirm if orderDetailsArray actually contains some value before you are accessing it. Also, you are just returning 1 for numberOfRowsInSection without even checking if orderDetailsArray has some content. This should possibly resolve your crash:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if (section == 0 || section == 1) {
if orderDetailsArray.count > 0 {
return 1
} else {
return 0
}
}
else {
return itemsArray.count
}
}
I am not sure if this is the kind of behavior you are expecting because it will result in section Headers with no rows but, you can modify that!
Need to register UINib in UITableView for use multiple custom cell in tableView.
How to register UINib in UITableView?
Just write below three line in ViewDidLoad()
tableView.register(UINib(nibName: "ShippingTableViewCell", bundle: nil), forCellReuseIdentifier: "shippingcell")
tableView.register(UINib(nibName: "OrderDetailsTableViewCell", bundle: nil), forCellReuseIdentifier: "orderdetailscell")
tableView.register(UINib(nibName: "OrdersTableViewCell", bundle: nil), forCellReuseIdentifier: "ordercell")
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