(Swift)如何通过重新加载 UITableView 根据 UITextfield 输入(动画)显示/隐藏单元格?

Posted

技术标签:

【中文标题】(Swift)如何通过重新加载 UITableView 根据 UITextfield 输入(动画)显示/隐藏单元格?【英文标题】:(Swift) How to (animate) show/hide cells based on UITextfield input by reloading a UITableView? 【发布时间】:2016-10-26 10:10:34 【问题描述】:

我是一名初级 ios 开发人员,我已经被一个问题困扰了很长一段时间了。

背景:我有一个带有表格视图的视图控制器。它包含 4 个动态原型单元:prototypecell 1 有一个 UITextField 和几个标签,prototypecell 2 有一个图像,prototypecell 3 和 4 只有一个标签。 (注:原型单元格 4 是基于对象中的一个数组,可以有 1 个或多个单元格)

打开屏幕后,只有第一个带有 UITextField 的单元格应该可见。当用户在此 UITextField 中输入一个数字(最多 3 位)时,必须比较该数字以检查它是否是对象数组中的现有数字。如果这个数字被证明是正确的,则必须发生两件事:

    第一个单元格中的标签需要更改布局(颜色、 字体大小, ...)。第一个单元格更改 rowHeight。 其他 3 个单元格必须出现并显示与数字对应的数据。

如果数字错误,则不会出现多余的单元格,并且第一个单元格会更新以告诉用户它不正确。

问题:我在根据第一个单元格中的数字输入为 3 个单元格出现和消失的方式设置动画时遇到问题。更具体地说,“toggleCellsVisibility”方法以及它与单元格创建的关系。

我尝试了多种组合:beginUpdates()、endUpdates()、reloadData()、reloadRowsAtIndexPath()、DispatchQueue.main.async 和 UIView.Animate()。但是我的代码工作的唯一方法是如果我实现如下的 toggleCellsVisibility 方法,它不会给我任何动画选项。

我似乎在创建和重新加载数据时做错了。我认为这与全局变量 indexOfObject 和 tempObject 的使用有关,它们在屏幕加载时保存虚拟数据,然后在给出正确数字时显示并用实际数据覆盖。

总结:当在第一个单元格中输入正确的数字时,我能否以某种方式插入/创建这 3 个单元格,同时还使用此特定设置为该更改设置动画?

补充: 我可以通过不使用 heightForRowAtIndexPath 来显示/隐藏额外的单元格吗?如果单元格可以自行调整大小会更好。

我选择并简化了相关的代码:

1) TableView 类:

class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, updateUITableView 

var objects: [Object] = [
    name: "test1", number: "test2", image: "imageName1", color: UIColor.black, array: ["test3", "test4"],
    [name: "test1", number: "test2", image: "imageName2", color: UIColor.white, array: ["test3", "test4", "test5"]
    //...
]

var cellsRowHeight = false

var indexOfObject: Int = 0
var tempObject = Object[name: "test1", number: "test2", color: UIColor.blue, image: "imageName3", array: ["test3"]]

@IBOutlet var tableView: UITableView!

// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int 
    return 1


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    let numberOfRows = 3 + tempObject.subArray.count
    return numberOfRows


//START - Input received from textfield in first cell
func checkNumberInput(senderCell: SearchInputTableViewCell, number: String) 
    // step 1: Check if number exists
    let match = checkNumberMatch(numberInput: number)

    // step 2: Change lay-out of input cell
    senderCell.changeLayout(numberMatch: match.0, name: match.1?.name, color: match.1?.color)

    // step 3: Show/hide cells
    toggleCellsVisibility(numberMatch: match.0)


//STEP 1: Check if the number corresponds to an existing number
func checkNumberMatch(numberInput: String) -> (Bool, Object?) 
    var returnObject: Object?
    var tuple = (false, returnObject)

    for (index, object) in objects.enumerated() where object.number == numberInput 
        returnObject = object

        tuple = (true, returnObject)

        tempObject = object
        indexOfObject = index
    
    return tuple


//STEP 3 = !!PROBLEM!!
func toggleCellsVisibility(numberMatch: Bool) 
    cellsRowHeight = numberMatch
    // if/else because of additional future implementation
    if numberMatch == true  //Cells appear
        DispatchQueue.main.async 
            self.tableView?.reloadData()
        

     else  //Cells disappear
        DispatchQueue.main.async 
            self.tableView?.reloadData()
        
    


//STEP 4:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
    switch (indexPath.row) 
    case 0 where !cellsRowHeight:
        return 187
    case 0 where cellsRowHeight:
        return 170
    case 1 where !cellsRowHeight:
        return 0
    case 1 where cellsRowHeight:
        return 170
    case 2 where !cellsRowHeight:
        return 0
    case 2 where cellsRowHeight:
        return 54
    case 3...200 where !cellsRowHeight:
        return 0
    case 3...200 where cellsRowHeight:
        return 44
    default:
        return 44
    


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    if indexPath.row == 0 
        let cellIdentifier = "InputCell"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! InputCell

        cell.delegate = self

        return cell
    
    else if indexPath.row == 1 
        let cellIdentifier = "Cell2"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell2

        cell.image?.image = UIImage(named: objects[indexOfObject].image)

        return cell
    
    else if indexPath.row == 2 
        let cellIdentifier = "Cell3"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell3

        cell.name?.text = objects[indexOfObject].name

        return cell
    
    else 
        let cellIdentifier = "Cell4"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell4

        cell.name?.text = objects[indexOfObject].subArray[(indexPath as IndexPath).row - 3]

        return cell
    

2) 细胞类:

protocol updateUITableView: class 
    func checkNumberInput(senderCell: SearchInputTableViewCell, number: String)


class InputCell: UITableViewCell, UITextFieldDelegate 

    var delegate: updateUITableView?

    @IBOutlet var textField: UITextField!
    @IBOutlet var nameLabel: UILabel!
    //... and other labels

    func changeLayout(numberMatch: Bool, name: String?, color: UIColor?) 
        if numberMatch == true 
            //lay-out changes to labels
        
        else 
            //lay-out changes to labels
        
    

    //Set maximum character limit in textField and dismiss keyboard when character limit (3) is reached.
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 
        let currentCharacterCount = textField.text?.characters.count ?? 0
        let newLength = currentCharacterCount + string.characters.count - range.length

        if (newLength == 3) 
            textField.text = (textField.text! as NSString).replacingCharacters(in: range, with: string)

            //Get text from textField
            let numberInput: String? = textField.text! //Get number if 3 characters entered
            if numberInput != nil 
                delegate?.checkNumberInput(senderCell: self, number: numberInput!)
            
            textField.resignFirstResponder()

            if (range.length + range.location > currentCharacterCount) 
                return false
             else 
                return true
            
        
        return true
    


class Cell2: UITableViewCell 
    @IBOutlet var image: UIImageView!


class Cell3: UITableViewCell 
    @IBOutlet var name: UILabel!


class Cell4: UITableViewCell 
    @IBOutlet var name: UILabel!

任何帮助将不胜感激!谢谢!

【问题讨论】:

为什么总是三个单元格?根据需要插入其他单元格(确保 numberOfRowsInSection 始终返回正确的数字) @Paulw11 屏幕始终至少有 1 个单元格(inputCell),但是当输入正确时,会添加额外的 3 个单元格:原型单元格 2 有 1 行带有图像,原型单元格 3 有具有名称和原型单元格的 1 行基于对象中的 subArray 具有未知数量的行。所有这些都必须能够根据输入改变高度。因此 numberOfRowsInSection 将在:1) 1 个单元格 (inputcell) 和 2) 1 个单元格 (inputcell) + 2(两个单元格都有 1 行)+ 基于对象中的子数组的数字之间变化所以我如何能够在对象之间动态切换这些? 【参考方案1】:

当您的操作完成后(检查输入是否与您想要的匹配),填充您用于 tableview 的数据源(您应该声明一个要用作数据源的空对象数组,并使用它),然后在您的 tableview 上使用 reloadData()。

对于单元格的高度,您可以使用tableView.rowHeight = UITableViewAutomaticDimension (https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html)

【讨论】:

以上是关于(Swift)如何通过重新加载 UITableView 根据 UITextfield 输入(动画)显示/隐藏单元格?的主要内容,如果未能解决你的问题,请参考以下文章

如何重新加载 UIPageViewController 以在 Swift 中重新加载其视图

Swift:如何使用导航控制器重新加载表格

如何重新加载UIPageViewController以在Swift中重新加载其视图

如何使用视图控制器重新加载 tableview (swift)

如何在 swift 中重新加载 UICollectionReusableView 中的集合视图

如何在swift 3中重新加载没有动画的特定单元格?