Search code examples
iosswiftuitableviewcustom-cell

Cannot dynamically add custom cell from 2nd data source


I've spent 20+ hours searching/trying different solutions just for this specific problem and cannot make it work. I'm just starting to learn Swift, so be gentile... I made sure to exhaust all options before asking for help.

I have 3 view controllers (VC1: form with 5 text fields, VC2: tableview for displaying gathered data, VC3: 2nd form with 3 text fields and an image picker)

screen shot of storyboard

VC1 gathers the text field data, pass to VC2, and use custom cell #1 to add to the tableview. I used the exact same methods to do the same for VC3 (changing the cell identifier, and using if/else to determine which section to add to) but cannot get any results. At first I thought my data wasn't being passed, but that checks out ("Finihed & Send" button set to alert and prints variable's text) next I thought it was my logic, but custom cell #1 works... I've been staring it this code for so long, I have horrible dreams about it. I feel like this should be obtainable with the techniques I'm applying but wonder if I am I wandering into Core Data territory but just don't know it.

tableview with only 1 custom cell

My intent is to have VC1 add a cell (custom cell 1) at indexPath.row 0 and VC3 to add (custom cell 2) indexPath.row >= 1. Everything works like I want it to with the exception of adding a second custom cell to the tableview.

addWorkViewController

import UIKit
import MapKit
import CoreLocation

class mainVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CLLocationManagerDelegate, UITextFieldDelegate {

    @IBOutlet weak var scrollviewWORK: UIScrollView!
    @IBOutlet weak var typeWORK: UISegmentedControl!
    @IBOutlet weak var locationWORK: UITextField!
    @IBOutlet weak var positionWORK: UISegmentedControl!
    @IBOutlet weak var priceWORK: UITextField!
    @IBOutlet weak var photo1WORK: UIImageView!
    @IBOutlet weak var descriptionWORK: UITextView!



/// Prepare Segues

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        switch segue.destination {

        case is WorkOverview:

                let workDest: WorkOverview = segue.destination as! WorkOverview
                var cost = ""
                var snap = UIImage()

                if (priceWORK.text == nil) {
                    cost = ""
                } else {
                    cost = priceWORK.text!
                }

                if (photo1WORK.image == nil) {
                    snap = UIImage()
                } else {
                    snap = photo1WORK.image!
                }

                workDest.workLocation = locationWORK.text!
                workDest.workDescription = descriptionWORK.text!
                workDest.workPrice = cost
                workDest.workPhoto = snap


            case is PlantList:

                let plantDest: PlantList = segue.destination as! PlantList
                plantDest.placeholder = ""

            default:
                break
        }
      }

/// END Segue Preparation

/// Save to List Button
    @IBAction func saveToListBTN(_ sender: UIButton) {
        performSegue(withIdentifier: "unwindToList", sender: self)
    }
/// END Save to List Button

/// Insert Plant

    @IBAction func insertPlant(_ sender: UIButton) {
        performSegue(withIdentifier: "toPlantListSegue", sender: self)
    }

    var addedPlant: String? = ""

/// END Insert Plant

/// Clear All Button

    @IBAction func clearAllBTN(_ sender: UIButton) {


    }

/// END Clear All Button


/// Segmented Controller - Work Type


    @IBAction func positionChanged(_ sender: UISegmentedControl) {
        switch positionWORK.selectedSegmentIndex {
        case 0:
            locationWORK.text? += " - Front"

        case 1:
            locationWORK.text? += " - Back"

        case 2:
            locationWORK.text? += " - Side"

        default:
            break
        }
    }


    @IBAction func indexChanged(_ sender: UISegmentedControl) {
        switch typeWORK.selectedSegmentIndex {
        case 0:
            descriptionWORK.text = "Provide and install "

        case 1:
            descriptionWORK.text = "Replace "

        case 2:
            descriptionWORK.text = "Remove and dispose of "

        default:
            break
        }
    }

/// END Segmented Controller - Work Type



    /// ScrollView Keyboard Adjust

    func textFieldDidBeginEditing(_ textField: UITextField) {
        if (textField == priceWORK){
            scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 205), animated: true)
        } else {}
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
    }

    /// END Scrollview Keyboard Adjust


/// VIEWDIDLOAD
    override func viewDidLoad() {
        super.viewDidLoad()

        // Toolbar
        let toolBar = UIToolbar()
        toolBar.sizeToFit()


        let backArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "backArrow"), style: .plain, target: nil, action: #selector(backArrowClicked))

        let spacerA = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)

        let workDoneBtn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneBtnClicked))

        let spacerB = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)

        let nextArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "nextArrow"), style: .plain, target: nil, action: #selector(nextArrowClicked))

        toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)


        toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)
        locationWORK.inputAccessoryView = toolBar
        priceWORK.inputAccessoryView = toolBar
        descriptionWORK.inputAccessoryView = toolBar

    }
/// END VIEWDIDLOAD


/// Toolbar - Done Button
    @objc func doneBtnClicked() {
        view.endEditing(true)
    }
/// END Toolbar - Done Button

/// Arrow to Next TextField
    @objc func nextArrowClicked() {

        if (locationWORK.isFirstResponder) {
            descriptionWORK.becomeFirstResponder()
        } else if (descriptionWORK.isFirstResponder) {
            priceWORK.becomeFirstResponder()
        } else if (priceWORK.isFirstResponder) {
            view.endEditing(true)
            scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
        }
    }
/// END Arrow to Next TextField

/// Arrow to Previous TextField
    @objc func backArrowClicked() {

        if (locationWORK.isFirstResponder) {
            view.endEditing(true)
            scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
        } else if (descriptionWORK.isFirstResponder) {
           locationWORK.becomeFirstResponder()
        } else if (priceWORK.isFirstResponder) {
            descriptionWORK.becomeFirstResponder()
        }
    }
/// END Arrow to Previous TextField



/// Image Select from Library & Camera

    @IBAction func takePhotoONE(_ sender: UIButton) {

        let imagePickerController = UIImagePickerController()
        imagePickerController.delegate = self

        let actionSheet = UIAlertController(title: "Want to add a photo?", message: "Please choose a source.", preferredStyle: .actionSheet)

        actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (action:UIAlertAction) in

            if UIImagePickerController.isSourceTypeAvailable(.camera) {
                imagePickerController.sourceType = .camera
                self.present(imagePickerController, animated: true, completion: nil)
            }else{
                print("Camera is not available")
            }

        }))

        actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary
            self.present(imagePickerController, animated: true, completion: nil)
        }))

        actionSheet.addAction(UIAlertAction(title: "Remove Photo", style: .destructive, handler: { (action:UIAlertAction) in self.photo1WORK.image = nil}))

        actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

        self.present(actionSheet, animated: true, completion: nil)

    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        let image = info[UIImagePickerControllerOriginalImage] as! UIImage
        photo1WORK.image = image
        picker.dismiss(animated: true, completion: nil)
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }

/// END Image Select from Library & Camera

/// GPS Location

        let addressManager = CLLocationManager()

        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

            let streetAddress = locations[0]
            CLGeocoder().reverseGeocodeLocation(streetAddress) { (placemark, error) in
                if error != nil
                {
                    print ("Sorry, there has been an error.")
                }
                else
                {
                    if let place = placemark?[0]
                    {
                        if place.subThoroughfare != nil
                        {
                            self.locationWORK.text = "\(place.subThoroughfare!) \(place.thoroughfare!)"
                            }
                        }
                    }
                }
            }

    @IBAction func getGPS(_ sender: UIButton) {

        // Address
        addressManager.delegate = self
        addressManager.desiredAccuracy = kCLLocationAccuracyBest
        addressManager.requestWhenInUseAuthorization()
        addressManager.startUpdatingLocation()

    }

 /// END GPS Location


}

TableViewController

import UIKit


class WorkOverview: UIViewController {


    @IBOutlet weak var listTableView: UITableView!

    @IBAction func addWorkBTN(_ sender: UIButton) {
    performSegue(withIdentifier: "overviewToWorkSegue", sender: self)
}

@IBAction func unwindToList(segue:UIStoryboardSegue) { }

    @IBAction func finishedBTN(_ sender: UIButton) {

        let alertController = UIAlertController(title: "Data Pass Test", message:
            workLocation, preferredStyle: UIAlertControllerStyle.alert)
        alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))

        self.present(alertController, animated: true, completion: nil)
    }

override func viewDidLoad() {
    super.viewDidLoad()

    property = createArray()
    workPJs = workArray()

    listTableView.delegate = self
    listTableView.dataSource = self

}

var propForm = String()
var propName = String()
var propCity = String()
var propDate = String()
var propDue = String()
var propRep = String()

    var workLocation = String()
    var workDescription = String()
    var workPrice = String()
    var workPhoto = UIImage()

var workPJs: [WorkManager] = []
var property: [TaskManager] = []

func workArray() -> [WorkManager] {

    var tempWork: [WorkManager] = []

    let work1 = WorkManager(location: workLocation, description: workDescription, price: workPrice, photo: workPhoto)
    tempWork.append(work1)
    return tempWork
}


func createArray() -> [TaskManager] {

    var tempProperty: [TaskManager] = []

    let prop1 = TaskManager(title: propForm, property: propName, city: propCity, date: propDate, due: propDue, rep: propRep)
    tempProperty.append(prop1)
    return tempProperty
}


}

extension WorkOverview: UITableViewDataSource, UITableViewDelegate {


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

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if section == 0 {
            return property.count
        } else {
            return workPJs.count
        }

    }

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

        if indexPath.section == 0 {
            let prop = property[indexPath.row]
            let cell = tableView.dequeueReusableCell(withIdentifier: "PropertyCell", for: indexPath) as! PropertyCell
            cell.setProperty(prop: prop)
            return cell
        } else if indexPath.section == 1 {
            let wrk = workPJs[indexPath.row]
            let cell = tableView.dequeueReusableCell(withIdentifier: "WorkCell", for: indexPath) as! WorkCell
            cell.setWork(wrk: wrk)
            return cell
        }
        return UITableViewCell()

     }



}

WorkCell class

import UIKit

class WorkCell: UITableViewCell {

    @IBOutlet weak var theLocation: UILabel!
    @IBOutlet weak var theDescription: UILabel!
    @IBOutlet weak var thePrice: UILabel!
    @IBOutlet weak var thePhoto: UIImageView!

    func setWork(wrk: WorkManager) {

        theLocation.text = wrk.location
        theDescription.text = wrk.description
        thePrice.text = wrk.price
        thePhoto.image = wrk.photo
    }
}

WorkManager class

import UIKit


class WorkManager {

    var location: String
    var description: String
    var price: String
    var photo: UIImage


    init(location: String, description: String, price: String, photo: UIImage){
        self.location = location
        self.description = description
        self.price = price
        self.photo = photo
    }



}

Solution

  • in ViewDidLoad add this code according to names of your cell

    tableView.registerNib(UINib(nibName: "cell xib", bundle: nil), forCellReuseIdentifier: "cell name")
    

    in your case you have to register both nib of your custom cell.

    i have feelings your section if else condition is wrong. better to debug.