Search code examples
swiftuitableviewfirebase-realtime-databaseuitextfieldcustom-cell

Use a table to display users added and their data


I am new to coding and have been trying to figure out the current problem I have for the last few weeks.

I have posted a bunch asking different variations of the same question and was informed the way I was trying to execute my idea was incorrect.

I am trying to send a collection of data to a list of users. The data involves a company name, the company's image, the day they need to make a decision and the day it was sent. I am currently able to send that information to my firebase database and firebase storage for the company image. The company image is also stored as a string under the company's information node.

What I was originally trying to accomplish was being able to create cells by pushing a button. The table would populate a cell with four textfields. A name textfield would be where I selected the user. Their firebase database name would show up in the textfield. I then could input text in three other textfields that corresponded to only that user. The user would be able to see the company info and the dates but then only their specific row data. I was told that I shouldn't be doing that and I decided to venture off to another possible solution.

Situation Now:

I have the same view controller set up with the company information and add button. That looks like:

viewcontroller

When the add button is pressed, a new view controller is shown with four textfields. The view controller name textfield allows for the user to select a user from the database and the text changes to that persons name. It looks like this:

viewcontroller2

I am using the name textfield to append an array which then is being used for the table in the first view controller to determine the number of rows. It is currently appending the array but not populating any cells. I plan on using labels to save the user and the other 3 textfields to the same row. But I can't even get any cells to populate.

Here is the code for the view controller that will display the users and their info in the table below the company information:

import UIKit
import Firebase

class ConsiderationsTestViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var tableView: UITableView!

static var numberOfPeople: [String] = []

var AddPersonCell = "AddPersonCell"

@IBOutlet weak var CompanyImage: UIImageView!

@IBOutlet weak var companyNameTextFieldConsiderations: UITextField!

@IBOutlet weak var companyDescriptionTextFieldConsiderations: UITextField!

@IBOutlet weak var startDateTextFieldConsiderations: UITextField!

@IBOutlet weak var endDateTextFieldConsiderations: UITextField!

let datePickerS = UIDatePicker()
let datePickerE = UIDatePicker()

    var database: Database!
    var storage: Storage!
    var selectedImage: UIImage?
    
    var ref:DatabaseReference?
    var databaseHandle:DatabaseHandle = 0
    
    let dbref = Database.database().reference()
    let uid = Auth.auth().currentUser?.uid
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
        
        self.companyNameTextFieldConsiderations.delegate = self
        self.companyDescriptionTextFieldConsiderations.delegate = self
        
        // Set the Firebase reference
        ref = Database.database().reference()
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ConsiderationsTestViewController.handleSelectCompanyImageView))
         CompanyImage.addGestureRecognizer(tapGesture)
         CompanyImage.isUserInteractionEnabled = true

        self.navigationController!.navigationBar.isTranslucent = false
        
        navigationItem.backBarButtonItem = UIBarButtonItem(
        title: "", style: .plain, target: nil, action: nil)
        
        createDatePickerForStart()
        
        createDatePickerForEnd()
        
    }

    @objc func handleSelectCompanyImageView() {

        let pickerController = UIImagePickerController()
        pickerController.delegate = self
        pickerController.allowsEditing = true
        present(pickerController, animated: true, completion: nil)
                    }
    
@IBAction func AddPersonTapped(_ sender: Any) {
  
}

@IBAction func sendButtonTapped(_ sender: Any) {
    
    let companyNameC = companyNameTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    let companyDescriptionC = companyDescriptionTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    
    let today = Date()
    let formatter1 = DateFormatter()
    formatter1.dateFormat = "MMM d y"
    print(formatter1.string(from: today))
    let todaysDate = formatter1.string(from: today)
            
    let storageRef = Storage.storage().reference(forURL: " MY STORAGE URL HERE")
    let imageName = companyNameTextFieldConsiderations.text!
    let storageCompanyRef = storageRef.child("Company_Image_Considerations").child("\(todaysDate)").child(imageName)
    let companyDescriptionTextFieldText = companyDescriptionTextFieldConsiderations.text
    let dateToStart = startDateTextFieldConsiderations.text
    let dateToDecide = endDateTextFieldConsiderations.text
    let companyRef = Database.database().reference().child("Considerations").child("\(todaysDate)").child(imageName)
    
    let index = IndexPath(row: 0,section: 0)
    let cell = tableView.cellForRow(at:index) as! ConsiderationsCell
    let nameTFC = cell.nameTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    let feedTFC = cell.feedTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    let storyTFC = cell.storyTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    let compensationTFC = cell.compensationTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    
    let values = ["Feed_Quantity": feedTFC, "Story_Quantity": storyTFC, "Compensation": compensationTFC]
    let considerationInfluencerRef = Database.database().reference().child("Considerations").child("\(todaysDate)").child(imageName).child("Users").child("\(nameTFC)")
    
        guard let imageSelected = self.CompanyImage.image else {
            print ("Avatar is nil")
            return
                }
                   
                var dict: Dictionary<String, Any> = [
                    "Company Image": "",
                    "Company Description": companyDescriptionTextFieldText!,
                    "Start Date": dateToStart!,
                    "Decision Date": dateToDecide! ]
    
                   
        guard let imageData = imageSelected.jpegData(compressionQuality: 0.5) else {
            return
                }
    
                let metadata = StorageMetadata()
                metadata.contentType = "image/jpeg"
                storageCompanyRef.putData(imageData, metadata: metadata, completion:
                { (StorageMetadata, error) in
                if (error != nil) {
                return
                    }
                            
                storageCompanyRef.downloadURL { (url, error) in
                if let metadateImage = url?.absoluteString {
                dict["Company Image"] = metadateImage
                   
                companyRef.updateChildValues(dict, withCompletionBlock:  {
                (error, ref) in
                if error == nil {
                print("Done")
                return
                    }
                  }
                )
               }
             }
                            
                                   storageRef.updateMetadata(metadata) { metadata, error in
                                    if error != nil {
                                    //Uh-oh, an error occurred!
                                    } else {
                                    // Updated metadata for 'images/forest.jpg' is returned
                                }
                            }
                        })

    considerationInfluencerRef.updateChildValues(values as [AnyHashable : Any]) { (error, ref) in
    if error != nil {
    print(error ?? "")
    return
                       }
    
self.navigationController?.popViewController(animated: true)
    }
}


func createDatePickerForStart() {
    // center text in field
    startDateTextFieldConsiderations.textAlignment = .center
    
    // toolbar
    let toolbar = UIToolbar()
    toolbar.sizeToFit()
    
    // barbutton
    let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressedStart))
    toolbar.setItems([doneButton], animated: true)
    
    // assign toolbar to textfield
    
    startDateTextFieldConsiderations.inputAccessoryView = toolbar
    
    // assign datePicker to text field
    startDateTextFieldConsiderations.inputView = datePickerS
    
    // date picker mode
    datePickerS.datePickerMode = .date
    
}

func createDatePickerForEnd() {
    // center text in field
    endDateTextFieldConsiderations.textAlignment = .center
    
    // toolbar
    let toolbar = UIToolbar()
    toolbar.sizeToFit()
    
    // barbutton
    let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressedEnd))
    toolbar.setItems([doneButton], animated: true)
    
    // assign toolbar to textfield
    endDateTextFieldConsiderations.inputAccessoryView = toolbar
    
    // assign datePicker to text field
    endDateTextFieldConsiderations.inputView = datePickerE
    
    // date picker mode
    datePickerE.datePickerMode = .dateAndTime
}

@objc func donePressedStart() {
    // formatter
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .none
    
    startDateTextFieldConsiderations.text = formatter.string(from: datePickerS.date)
    self.view.endEditing(true)
}

@objc func donePressedEnd() {
    // formatter
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .medium
    
    endDateTextFieldConsiderations.text = formatter.string(from: datePickerE.date)
    self.view.endEditing(true)
   }
}
    
extension ConsiderationsTestViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
               func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
                   //print("did Finish Picking Media")
                   if let image = info[UIImagePickerController.InfoKey(rawValue: "UIImagePickerControllerEditedImage")] as? UIImage{
                       selectedImage = image
                       CompanyImage.image = image
                   }
                   dismiss(animated: true, completion: nil)
               }
    }

extension ConsiderationsTestViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return ConsiderationsTestViewController.numberOfPeople.count
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: AddPersonCell, for: indexPath) as! ConsiderationsCell
    
    return cell
  }

}

Here is the code for the view controller where I select the user and input their respective information:

import UIKit
import Firebase

class DropDownCellViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UITextFieldDelegate {

var numberOfPeople = [String]()

var users = [User]()

var userName = [String]()

var filteredNames: [String]!

let dropDownCell = "dropDownCell"

var emptyField = [String]()

@IBOutlet weak var nameTextField: NoCopyPasteUITextField!

@IBOutlet weak var tableView: UITableView!

@IBOutlet weak var gridTextField: UITextField!

@IBOutlet weak var storyTextField: UITextField!

@IBOutlet weak var compensationTextField: UITextField!

@IBAction func textFieldChanged(_ sender: AnyObject) {
    tableView.isHidden = true
}

@IBAction func cancelButtonTapped(_ sender: Any) {
    self.navigationController?.popViewController(animated: true)
}

@IBAction func addButtonTapped(_ sender: Any) {
    
    ConsiderationsTestViewController.numberOfPeople.append("\(nameTextField.text!)")
    
    self.navigationController?.popViewController(animated: true)
    
    print(ConsiderationsTestViewController.numberOfPeople)
}

let searchController = UISearchController(searchResultsController: nil)

override func viewDidLoad() {
    super.viewDidLoad()
    
    updateDataArray()
    
    tableView.register(UserCell.self, forCellReuseIdentifier: dropDownCell)
    
    filteredNames = userName
    
        tableView.delegate = self
        tableView.dataSource = self
        nameTextField.delegate = self
           
        tableView.isHidden = true
           
           // Manage tableView visibility via TouchDown in textField
    nameTextField.addTarget(self, action: #selector(textFieldActive), for: UIControl.Event.touchDown)
       }

 override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
 }

 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
  {
      guard let touch:UITouch = touches.first else
      {
          return;
      }
      if touch.view != tableView
      {
          nameTextField.endEditing(true)
          tableView.isHidden = true
      }
  }

 @objc func textFieldActive() {
       tableView.isHidden = !tableView.isHidden
   }

  // MARK: UITextFieldDelegate
   func textFieldDidEndEditing(_ textField: UITextField) {
       // TODO: Your app can do something when textField finishes editing
       print("The textField ended editing. Do something based on app requirements.")
   }
   
   func textFieldShouldReturn(_ textField: UITextField) -> Bool {
       textField.resignFirstResponder()
       return true
   }


   func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    
    return filteredNames.count
}

   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: dropDownCell, for: indexPath) as!
    UserCell
    
    let user = users[indexPath.row]
    cell.textLabel?.text = user.name
    
    if let profileImageUrl = user.profileImageUrl {
    
    cell.profileImageView.loadImageUsingCacheWithUrlString(urlString: profileImageUrl)
        
    }
    return cell
}

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72
       }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // Row selected, so set textField to relevant value, hide tableView
           // endEditing can trigger some other action according to requirements
           nameTextField.text = userName[indexPath.row]
           tableView.isHidden = true
           nameTextField.endEditing(true)
       }

    //Mark: Search Bar Config
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    
    filteredNames = []
    
    if searchText == "" {
        filteredNames = userName
        
     }
    else {
        for names in userName {
            
            if names.lowercased().contains(searchText.lowercased()) {
                
                filteredNames.append(names)
            }
        }
    }
    self.tableView.reloadData()
     }

    func updateDataArray() {
    
    Database.database().reference().child("users").observe(.childAdded, with: { (snapshot) in
        
        if let dictionary = snapshot.value as? [String: AnyObject] {
            
            let user = User()
            //self.setValuesForKeys(dictionary)
            user.name = dictionary["name"] as? String
            user.email = dictionary["email"] as? String
            user.facebookUrl = dictionary["facebookUrl"] as? String
            user.instagramUrl = dictionary["instagramUrl"] as? String
            user.linkedInUrl = dictionary["linkedInUrl"] as? String
            user.profileImageUrl = dictionary["profileImageUrl"] as? String
            user.twitterUrl = dictionary["twitterUrl"] as? String
            user.id = dictionary["id"] as? String
            user.userType = dictionary["userType"] as? String
            
            self.users.append(user)
            self.userName.append(user.name!)
            self.filteredNames.append(user.name!)
            
            DispatchQueue.main.async {
            self.tableView.reloadData()
            }
        }
        
       }, withCancel: nil)
    
    
    }
   }

I had a button in the original view controller that would print the array. I tested it before attempting to append the array. It was empty. I then added to people and tested it. It printed both the names but the table didn't change. I tried to dispatch.resync in the add button but it's too ambiguous. I really could use the help. Thanks! And thank you for the patience. I am new to coding and have no education in coding besides being self taught.


Solution

  • I found out how to reload the data. I just need to figure out how to set the labels equal to the textfield text. That way I can see it and be able to send it to firebase. The way I fixed it was by having my add button have the following code:

    @IBAction func addButtonTapped(_ sender: Any) {
        
        ConsiderationsTestViewController.numberOfPeople.append("\(nameTextField.text!)")
        
        self.navigationController?.popViewController(animated: true)
        
        print(ConsiderationsTestViewController.numberOfPeople)
        
        DispatchQueue.main.async {
           NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
                }
    }
    

    I then inserted this in the viewDidLoad of the view controller I wanted to reload:

     NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)