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
Related
I am trying to get the inputted text from recipeName, servingsNumber, prepTime and cookTime to save to fb.
I know this is close to where I need to be. I am need some help with my save function. I need to assign the textField.text in sendToFirebase() I am able to print out the inputted text into the console and I was able to get it to save a document to FB but the entry was null. What am I missing? I feel like it is here in my code:
import UIKit
import Firebase
class AddRecipeViewController: UIViewController {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var saveButton: UIBarButtonItem!
let db = Firestore.firestore()
var id = UUID().uuidString
var data = [RecipeData]()
let user = Auth.auth().currentUser?.email
var objRecipe = RecipeData.init()
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
navigationItem.hidesBackButton = true
let newBackButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelPressed))
self.navigationItem.leftBarButtonItem = newBackButton
table.register(UINib(nibName: "RecipeNameCell", bundle: nil), forCellReuseIdentifier: "recipeName")
table.register(UINib(nibName: "ServingSizeTableViewCell", bundle: nil), forCellReuseIdentifier: "servings")
table.register(UINib(nibName: "PrepTimeTableViewCell", bundle: nil), forCellReuseIdentifier: "prep")
table.register(UINib(nibName: "CookTimeTableViewCell", bundle: nil), forCellReuseIdentifier: "cook")
}
func sendToFirebase() {
do {
let recipeName = objRecipe.recipeName
let servings = objRecipe.servingsNumber
let prep = objRecipe.prepTime
let cook = objRecipe.cookTime
let user = Auth.auth().currentUser?.email
let id = objRecipe.id
let data = try JSONEncoder().encode(objRecipe)
let dictionary = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] ?? [:]
db.collection("Recipe").document(objRecipe.id).setData(dictionary)
}
catch {
print(error)
}
}
#objc func cancelPressed(sender: UIBarButtonItem) {
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
}
#IBAction func savePressed(_ sender: UIBarButtonItem) {
sendToFirebase()
}
}
extension AddRecipeViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = table.dequeueReusableCell(withIdentifier: "recipeName", for: indexPath) as! RecipeNameCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else if indexPath.row == 1 {
let cell = table.dequeueReusableCell(withIdentifier: "servings", for: indexPath) as! ServingSizeTableViewCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else if indexPath.row == 2 {
let cell = table.dequeueReusableCell(withIdentifier: "prep", for: indexPath) as! PrepTimeTableViewCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else if indexPath.row == 3{
let cell = table.dequeueReusableCell(withIdentifier: "cook", for: indexPath) as! CookTimeTableViewCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else {
let cell = table.dequeueReusableCell(withIdentifier: "recipeName", for: indexPath) as! RecipeNameCell
return cell
}
}
}
extension AddRecipeViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
switch textField.tag {
case 0:
let recipeName = textField.text!
print(recipeName)
objRecipe.recipeName = textField.text!
// sendToFirebase() // Send to firebase
case 1:
let servingSize = textField.text!
print(servingSize)
objRecipe.servingsNumber = textField.text!
// sendToFirebase() // Send to firebase
case 2:
let prepTime = textField.text!
print(prepTime)
objRecipe.prepTime = textField.text!
// sendToFirebase() // Send to firebase
case 3:
let cookTime = textField.text!
print(cookTime)
objRecipe.cookTime = textField.text!
// sendToFirebase() // Send to firebase
default: break
}
}
}
struct RecipeData: Codable {
var user: String?
var recipeName: String?
var ingredientsText: String?
var directionsText: String?
var servingsNumber: String?
var prepTime: String?
var cookTime: String?
var image: String?
let id = UUID().uuidString
}
class RecipeNameCell: UITableViewCell {
#IBOutlet weak var textField: UITextField!
var id = UUID().uuidString
let db = Firestore.firestore()
var name: String?
override func awakeFromNib() {
super.awakeFromNib()
textField.placeholder = "Recipe Name..."
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
func saveData(text: String) {
if let recipeName = recipeNameTextField.text,
let addedIngredients = ingredientsTextField.text,
let directionsText = directionsTextField.text,
let servingsNum = numServingLabel.text,
let image = imageView.image,
let user = Auth.auth().currentUser?.email {
let newRecipeRef = db.collection(D.FStore.collectionName).document(id)
newRecipeRef.setData([
D.FStore.recipeTextField: recipeName,
D.FStore.ingredientsText: addedIngredients,
D.FStore.directionsText: directionsText,
D.FStore.numberServings: servingsNum,
D.FStore.userField: user,
D.FStore.id: id,
D.FStore.image: image,
]) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID:\(newRecipeRef)")
}
}
}
}
import UIKit
import Firebase
class HomeScreenViewController: UIViewController {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var logout: UIBarButtonItem!
#IBOutlet weak var add: UIBarButtonItem!
let db = Firestore.firestore()
var data = [RecipeData]()
var recipeNamed: String?
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
navigationItem.hidesBackButton = true
table.register(UINib(nibName: "RecipeCell", bundle: nil), forCellReuseIdentifier: "cell")
}
func loadRecipeNames() {
db.collection("Recipe")
.addSnapshotListener { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
if let user = data["User"] as? String,
let recipeNameLabels = data["Recipe Name"] as? String,
let prep = data["Prep Time"] as? String,
let cook = data["Cook Time"] as? String,
let servings = data["Servings"] as? String
{
let newRecipe = RecipeData(user: user, recipeName: recipeNameLabels, servingsNumber: servings, prepTime: prep, cookTime: cook)
self.data.append(newRecipe)
}
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
}
}
#IBAction func logoutPress(_ sender: UIBarButtonItem) {
do {
try Auth.auth().signOut()
navigationController?.popToRootViewController(animated: true)
} catch let signOutError as NSError {
print("Error signing out: %#", signOutError)
}
}
#IBAction func addRecipePressed(_ sender: UIBarButtonItem) {
performSegue(withIdentifier: "add", sender: self)
}
}
extension HomeScreenViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = data[indexPath.row]
let cell = table.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! RecipeCell
cell.recipeNameLabel.text = data.recipeName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "DetailSegue", sender: self)
}
}
You need to fire the sendToFirebase() function:
extension AddRecipeViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
switch textField.tag {
case 0:
let recipeName = textField.text!
print(recipeName)
objRecipe.recipeName = textField.text!
sendToFirebase() // Send to firebase
case 1:
let servingSize = textField.text!
print(servingSize)
objRecipe.servingsNumber = textField.text!
sendToFirebase()
case 2:
let prepTime = textField.text!
print(prepTime)
objRecipe.prepTime = textField.text!
sendToFirebase()
case 3:
let cookTime = textField.text!
print(cookTime)
objRecipe.cookTime = textField.text!
sendToFirebase()
default: break
}
}
}
sendToFirebase() function:
func sendToFirebase() {
let recipeName = objRecipe.recipeName
let servings = objRecipe.servingsNumber
let prep = objRecipe.prepTime
let cook = objRecipe.cookTime
let user = Auth.auth().currentUser?.email
let newRecipeRef = db.collection("Recipe").document(objRecipe.id)
newRecipeRef.setData([
"Recipe Name" : recipeName ?? "Empty",
"Serving #" : servings ?? "Empty",
"Prep Time" : prep ?? "Empty",
"Cook Time" : cook ?? "Empty",
"User" : user ?? "No email provided",
])
{ err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID:\(newRecipeRef)")
}
}
}
How should I get the document id upon tapping the UItableViewCell?
I know the below code do give me the row index, but for a feed post I would like to get the document id for the the particular post everytime
I have tried to retrieve the document id through key setup in model file but avail to no good
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("row selected: \(indexPath.row)")
performSegue(withIdentifier: "toDetailView", sender: indexPath)
}
Posts Model
import Foundation
import Firebase
import FirebaseFirestore
protocol DocumentSerializable {
init?(dictionary:[String:Any])
}
struct Post {
var _username: String!
var _postTitle: String!
var _postcategory: String!
var _postContent: String!
var dictionary:[String:Any] {
return [
"username": _username,
//"profile_pic":profile_pic,
"postTitle":_postTitle,
"postcategory":_postcategory,
"postContent":_postContent
]
}
}
extension Post : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let username = dictionary["username"] as? String,
// let profile_pic = dictionary["profile_pic"] as? String,
let postTitle = dictionary["postTitle"] as? String,
let postcategory = dictionary["postcategory"] as? String,
let postContent = dictionary["postContent"] as? String else { return nil }
self.init( _username: username ,_postTitle: postTitle, _postcategory: postcategory, _postContent: postContent)
}
}
Code to retrieve the collection as whole into. tableview
import Foundation
import UIKit
import Firebase
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView:UITableView!
var posts = [Post]()
var db: Firestore!
var postKey:String = ""
private var documents: [DocumentSnapshot] = []
//public var posts: [Post] = []
private var listener : ListenerRegistration!
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
self.navigationController?.navigationBar.isTranslucent = false
tableView = UITableView(frame: view.bounds, style: .plain)
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
tableView.backgroundColor = UIColor(white: 0.90,alpha:1.0)
view.addSubview(tableView)
var layoutGuide:UILayoutGuide!
if #available(iOS 11.0, *) {
layoutGuide = view.safeAreaLayoutGuide
} else {
// Fallback on earlier versions
layoutGuide = view.layoutMarginsGuide
}
tableView.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: layoutGuide.topAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor).isActive = true
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
retrieveAllPosts()
//checkForUpdates()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/* override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("toDetailPage", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = self.tableView.indexPathForSelectedRow
let person = personList[indexPath!.row]
if segue.identifier == "toDetailPage"{
let DetailBookViewController = (segue.destinationViewController as! DetailPage)
DetailBookViewController.user_name = user_name
DetailBookViewController.user_age = user_age
DetailBookViewController.user_urlPicture = user_urlPicture
}*/
#IBAction func handleLogout(_ sender:Any) {
try! Auth.auth().signOut()
self.dismiss(animated: false, completion: nil)
}
/*func checkForUpdates() {
db.collection("posts").whereField("timeStamp", isGreaterThan: Date())
.addSnapshotListener {
querySnapshot, error in
guard let snapshot = querySnapshot else {return}
snapshot.documentChanges.forEach {
diff in
if diff.type == .added {
self.posts.append(Post(dictionary: diff.document.data())!)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}*/
func retrieveAllPosts(){
let postsRef = Firestore.firestore().collection("posts").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()
self.postKey = document.documentID
let username = data["username"] as? String ?? ""
let postTitle = data["postTitle"] as? String ?? ""
let postcategory = data["postcategory"] as? String ?? ""
let postContent = data["postContent"] as? String ?? ""
let newSourse = Post(_postKey:self.postKey, _username: username, _postTitle: postTitle, _postcategory: postcategory, _postContent: postContent)
self.posts.append(newSourse)
print(self.postKey)
}
self.tableView.reloadData()
}
}
}
}
/* postsRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
self.posts = querySnapshot!.documents.flatMap({Post(dictionary: $0.data())})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}*/
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.set(post: posts[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("row selected: \(indexPath.row)")
//performSegue(withIdentifier: "toDetailView", sender: indexPath)
}
}
Add a new documentId property to your Post struct:
struct Post {
var _username: String!
var _postTitle: String!
var _postcategory: String!
var _postContent: String!
var _documentId: String! // This is new.
var dictionary:[String : Any] {
return [
"documentId" : _documentId, // This is new.
"username": _username,
//"profile_pic":profile_pic,
"postTitle":_postTitle,
"postcategory":_postcategory,
"postContent":_postContent
]
}
}
extension Post : DocumentSerializable
{
init?(dictionary: [String : Any])
{
guard let username = dictionary["username"] as? String,
// let profile_pic = dictionary["profile_pic"] as? String,
let postTitle = dictionary["postTitle"] as? String,
let postcategory = dictionary["postcategory"] as? String,
let documentId = dictionary["documentId"] as? String // This is new.
let postContent = dictionary["postContent"] as? String else { return nil }
self.init( _username: username ,_postTitle: postTitle, _postcategory: postcategory, _postContent: postContent, _documentId: documentId)
}
}
Change your retrieveAllPosts function and set the documentId of the Post instance, do not use the global variable for this:
if let snapshot = snapshot
{
for document in snapshot.documents
{
let data = document.data()
let username = data["username"] as? String ?? ""
let postTitle = data["postTitle"] as? String ?? ""
let postcategory = data["postcategory"] as? String ?? ""
let postContent = data["postContent"] as? String ?? ""
let newSourse = Post(_postKey:self.postKey, _username: username, _postTitle: postTitle, _postcategory: postcategory, _postContent: postContent, _documentId: document.documentId)
self.posts.append(newSourse)
}
}
Now you can access the documentId of the selected Post in didSelectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let post = self.posts[indexPath.row]
Swift.print(post.documentId)
// print("row selected: \(indexPath.row)")
// performSegue(withIdentifier: "toDetailView", sender: indexPath)
}
Hopefully this will guide you into the right direction.
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.
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
}