重新加载表格视图单元格,保存状态,更改高度

时间:2018-09-03 01:17:07

标签: ios swift

我在tableview中有一个tableviewcell。单击复选框后,应显示/隐藏字段,并调整单元格高度以支持更改。

当前,一切正常,直到我重新加载电池以更改高度为止。不仅需要两次轻击才能工作,而且还删除了字段的输入值。好像它在两个不同的单元之间进行交换。

下面是显示问题的gif图片。这两个视图控制器的代码如下。

enter image description here

ViewController

import UIKit

class CheckoutViewController: UIViewController {

    let cellSpacingHeight:CGFloat = 30.0

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        tableView.delegate = self
        tableView.dataSource = self
        tableView.separatorStyle = .none
        tableView.allowsSelection = false
        tableView.keyboardDismissMode = .onDrag

        NotificationCenter.default.addObserver(self, selector: #selector(CheckoutViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(CheckoutViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)

    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: Keyboard Notifications
    @objc func keyboardWillShow(notification: NSNotification) {
        if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
            tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
        }
    }

    @objc func keyboardWillHide(notification: NSNotification) {
        UIView.animate(withDuration: 0.2, animations: {
            // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
            self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
        })
    }

    func foo(){

    }


}

// datasource
extension CheckoutViewController: UITableViewDataSource{

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

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

    // Set the spacing between sections
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return cellSpacingHeight
    }

    // Make the background color show through
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = UIView()
        headerView.backgroundColor = UIColor.clear
        return headerView
    }

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

        if(indexPath.section == 0){
            let cellID = "CheckoutEmailTableViewCellReuseID"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! CheckoutEmailTableViewCell

            cell.setUp()

            return cell
        }else if(indexPath.section == 1){
            let cellID = "CheckoutAddressTableViewCellReuseID"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! CheckoutAddressTableViewCell

            cell.setUp(setUpCase: .shipping)
            cell.headerLabel.text = "SHIPPING INFORMATION"

            return cell
        }else if(indexPath.section == 2){
            let cellID = "CheckoutAddressTableViewCellReuseID"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! CheckoutAddressTableViewCell

            cell.setUp(setUpCase: .billing)
            cell.headerLabel.text = "BILLING INFORMATION"



            cell.onMyAction = {
                print("cell.sameAsButtonIsChecked: \(cell.sameAsButtonIsChecked)")
                if let indexPath = tableView.indexPath(for: cell) {
                    tableView.reloadRows(at: [indexPath], with: .automatic)
                }
            }

            return cell
        }else if(indexPath.section == 3){
            let cellID = "CheckoutPaymentMethodTableViewCellReuseID"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! CheckoutPaymentMethodTableViewCell

            cell.setUp()

            return cell
        }else if(indexPath.section == 4){
            let cellID = "CheckoutTotalTableViewCellReuseID"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! CheckoutTotalTableViewCell

            return cell
        }else if(indexPath.section == 5){
            let cellID = "CheckoutButtonTableViewCellReuseID"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! CheckoutButtonTableViewCell

            return cell
        }

        return UITableViewCell()
    }


}

// delegate
extension CheckoutViewController:UITableViewDelegate {}

TableViewCell

import Foundation
import UIKit

class CheckoutAddressTableViewCell: UITableViewCell {

    let addressFieldsHeight:CGFloat = 330

    typealias CellAction = () -> Void
    var onMyAction: CellAction?

    var initialized = false
    var sameAsButtonIsChecked:Bool = true {
        willSet{
            print("will:sameAsButtonIsChecked: \(sameAsButtonIsChecked)")
        }
        didSet {
            print("didStart:sameAsButtonIsChecked: \(sameAsButtonIsChecked)")
            if sameAsButtonIsChecked  {
                print("if")
                sameAsCheckboxImageView.image = #imageLiteral(resourceName: "iconCheckboxChecked")
                addressFieldsView.isHidden = true
                addressFieldsHeightConstraint.constant = 0
            }else{
                print("else")
                sameAsCheckboxImageView.image = #imageLiteral(resourceName: "iconCheckbox")
                addressFieldsView.isHidden = false
                addressFieldsHeightConstraint.constant = addressFieldsHeight
            }
            print("didFinish:sameAsButtonIsChecked: \(sameAsButtonIsChecked)")
            onMyAction?()
        }
    }


    @IBOutlet weak var headerLabel: UILabel!
    @IBOutlet weak var sameAsCheckboxView: UIView!
    @IBOutlet weak var sameAsCheckboxImageView: UIImageView!
    @IBOutlet weak var addressFieldsView: UIView!

    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var addressOneTextField: UITextField!
    @IBOutlet weak var addressTwoTextField: UITextField!
    @IBOutlet weak var cityTextField: UITextField!
    @IBOutlet weak var stateTextField: UITextField!
    @IBOutlet weak var zipCodeTextField: UITextField!
    @IBOutlet weak var countryTextField: UITextField!
    @IBOutlet weak var phoneTextField: UITextField!

    @IBOutlet weak var stateImageView: UIImageView!
    @IBOutlet weak var countryImageView: UIImageView!


    @IBOutlet weak var sameAsHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var addressFieldsHeightConstraint: NSLayoutConstraint!
    enum SetUpCase {
        case shipping
        case billing
    }

    func setUp(setUpCase:SetUpCase) {
        if(setUpCase == .shipping){
            sameAsHeightConstraint.constant = 0
            sameAsCheckboxView.isHidden = true
        }else if(setUpCase == .billing){
            if !initialized {
                sameAsButtonIsChecked = true
            }
        }

        setUpTextFields()

        sameAsCheckboxView.addTapGesture(tapNumber: 1, target: self, action: #selector(sameAsTap))
        stateImageView.addTapGesture(tapNumber: 1, target: self, action: #selector(focusStateTextField))
        countryImageView.addTapGesture(tapNumber: 1, target: self, action: #selector(focusCountryTextField))

        initialized = true;
    }




    private func setUpTextFields(){
        nameTextField.delegate = self
        addressOneTextField.delegate = self
        addressTwoTextField.delegate = self
        cityTextField.delegate = self
        stateTextField.delegate = self
        zipCodeTextField.delegate = self
        countryTextField.delegate = self
        phoneTextField.delegate = self

        nameTextField.setBothPaddingPoints(10, 10)
        addressOneTextField.setBothPaddingPoints(10, 10)
        addressTwoTextField.setBothPaddingPoints(10, 10)
        cityTextField.setBothPaddingPoints(10, 10)
        stateTextField.setBothPaddingPoints(10, 10)
        zipCodeTextField.setBothPaddingPoints(10, 10)
        countryTextField.setBothPaddingPoints(10, 10)
        phoneTextField.setBothPaddingPoints(10, 10)
    }

    @objc func sameAsTap(){
        self.endEditing(true)
        sameAsButtonIsChecked = !sameAsButtonIsChecked
    }


    @objc func focusStateTextField(){
        stateTextField.becomeFirstResponder()
    }
    @objc func focusCountryTextField(){
        countryTextField.becomeFirstResponder()
    }

}

// Unfortunately we need to tell iOS explicitly where to go next and
// explicitly where to look after the return key
extension CheckoutAddressTableViewCell: UITextFieldDelegate{
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if(textField.returnKeyType == .next){
            if(textField == stateTextField || textField == countryTextField){
                textField.superview?.superview?.superview?.viewWithTag(textField.tag+1)?.becomeFirstResponder()
            }else{
                textField.superview?.superview?.viewWithTag(textField.tag+1)?.becomeFirstResponder()
            }
        }else{
            textField.resignFirstResponder()
        }

        return true
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
//        textField
    }
}

偶尔出现错误消息(我认为这是一个单独的问题,因为它并不总是显示)

2018-09-03 09:52:48.642169+0900 nameOfApp[33568:2874030] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x60000029e910 UIView:0x7f9e36d33aa0.height == 40   (active)>",
    "<NSLayoutConstraint:0x60000029f180 UIView:0x7f9e36dd5260.height == 63   (active)>",
    "<NSLayoutConstraint:0x60000049d970 UIView:0x7f9e36dd5960.height == 0   (active)>",
    "<NSLayoutConstraint:0x60000049e0f0 V:|-(0)-[UIView:0x7f9e36d33aa0]   (active, names: '|':UIView:0x7f9e36d8f380 )>",
    "<NSLayoutConstraint:0x60000049e230 V:[UIView:0x7f9e36d33aa0]-(0)-[UIView:0x7f9e36dd5260]   (active)>",
    "<NSLayoutConstraint:0x60000049e320 V:[UIView:0x7f9e36dd5260]-(0)-[UIView:0x7f9e36dd5960]   (active)>",
    "<NSLayoutConstraint:0x60000049e370 V:[UIView:0x7f9e36dd5960]-(0)-|   (active, names: '|':UIView:0x7f9e36d8f380 )>",
    "<NSLayoutConstraint:0x60000049e460 V:[UIView:0x7f9e36d8f380]-(0)-|   (active, names: '|':UITableViewCellContentView:0x7f9e36d90ed0 )>",
    "<NSLayoutConstraint:0x60000049e4b0 V:|-(0)-[UIView:0x7f9e36d8f380]   (active, names: '|':UITableViewCellContentView:0x7f9e36d90ed0 )>",
    "<NSLayoutConstraint:0x600000683480 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7f9e36d90ed0.height == 433   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x60000029f180 UIView:0x7f9e36dd5260.height == 63   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

1 个答案:

答案 0 :(得分:1)

您应通过以下方法添加textField.text = "text kept when hidden"

func setUp(setUpCase:SetUpCase) {
        if(setUpCase == .shipping){
            sameAsHeightConstraint.constant = 0
            sameAsCheckboxView.isHidden = true
        }else if(setUpCase == .billing){
            if !initialized {
                sameAsButtonIsChecked = true
            }
        }

        setUpTextFields()

        sameAsCheckboxView.addTapGesture(tapNumber: 1, target: self, action: #selector(sameAsTap))
        stateImageView.addTapGesture(tapNumber: 1, target: self, action: #selector(focusStateTextField))
        countryImageView.addTapGesture(tapNumber: 1, target: self, action: #selector(focusCountryTextField))

        initialized = true;
    }

sameAsCheckboxView.addTapGesture(tapNumber: 1, target: self, action: #selector(sameAsTap))提取到awakeFromNib:更好。

NSLayoutConstraint错误应使用xib文件调试。

因为UITableViewCell是可重用的。因此,您可以从该元素中隐藏该单元格的元素,然后再次显示它。

cell.onMyAction = {
                print("cell.sameAsButtonIsChecked: \(cell.sameAsButtonIsChecked)")
                if let indexPath = tableView.indexPath(for: cell) {
                    tableView.reloadRows(at: [indexPath], with: .automatic)
                }
            }

下面的代码称为

let cellID = "CheckoutAddressTableViewCellReuseID"
let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! CheckoutAddressTableViewCell

cell.setUp(setUpCase: .billing)

并且您没有保留textField的文本。因此,总是出现占位符。