UITableViewCell повторяется и теряет свои данные при прокрутке

Я создал форму, в которой я использую Textfields и TextField Dropdown. Во время прокрутки значения текстовых полей TableView теряются, а для раскрывающихся значений текстового поля автоматически устанавливается значение по умолчанию.

Вот мой код:

    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))
        })
    }}

2 ответа

При использовании UITableView with reusable cell identifier, вы должны хранить входные данные вашего UITextField или любые другие поля ввода, когда редактирование завершено в ваш источник данных, который вы используете для заполнения всех ячеек. Поскольку многоразовая ячейка сама управляет памятью для отрисовки всех ваших элементов управления, то нет никаких гарантий, что ваши данные останутся такими же, как и при прокрутке таблицы.

В этом if - else выражение

if dict["value"] as? String == "" {
   cell.txtInput.setDate(Date(), animated: true)
} else{
   cell.txtInput.selectedItem = dict["value"] as? String
}

Вы устанавливаете два разных элемента пользовательского интерфейса в зависимости от значения словаря. При повторном использовании ячеек эти состояния пользовательского интерфейса будут оставаться до тех пор, пока они не будут изменены в следующий раз.

Вы должны убедиться, что каждый элемент пользовательского интерфейса установлен в определенное состояние, чтобы избежать непредвиденного поведения, поэтому вам также нужно установить другой элемент пользовательского интерфейса. Например ([default value] это просто заполнитель).

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
}

Если нет уникального default value Вы должны добавить свойства в источник данных, чтобы сохранить текущие состояния даты и выбранного элемента.

Что касается текстового поля, вы должны обновить источник данных после изменения текста, потому что в cellForRow текстовое поле всегда обновляется из источника данных.

И - как всегда - не использовать NS(Mutable)Array а также NS(Mutable)Dictionary в Свифте. Вы отбрасываете важную информацию о типах, и изменяемые типы коллекций не связаны с нативными типами Swift. Наконец вы можете удалить все break линии в switch выражения (кроме как после default). Они избыточны в Swift.

Другие вопросы по тегам