加载未显示的UITableViewCell

时间:2017-09-13 22:39:56

标签: ios swift xcode uitableview

我遇到UITableViewCells无法加载的问题,除非我向下滚动显示它们,问题是每个自定义单元格中都有一个切换开关,如果它已打开,那么我添加该单元格&# 39;计算的值,但计算不完整,因为它缺少一些未加载的单元格(并且在我向下滚动时加载它们)。
希望这是有道理的。这是在蓝牙连接应用程序上,它应该在每次收到新值时刷新表。这是加载单元格的代码:

//TABLE LOADING / RELOADING FUNCTIONS:
func numberOfSections(in tableView: UITableView) -> Int {
    return 1

}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        return temperatureReadingArray.count //Should return 1 or 9 depending on array received
}

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

    let cell = tableView.dequeueReusableCell(withIdentifier: temperatureReading) as! TemperatureReading

    cell.channelLabel.text = channelNames[indexPath.row]
    cell.readingLabel.text = temperatureReadingArray[indexPath.row]

    if (channelNames.count == 1)
    {
     cell.toggleSwitch.isHidden = true
    }
    if (cell.toggleSwitch.isOn)
    {
        if let value = Double(cell.readingLabel.text!)
        {
            print("valid number: \(value), appending")
            TNUArray.append(value)
        }
        else
        {
            print("Not a valid number reading")
        }
    }
    return cell
}

正如您所看到的,我检查了切换开关,到目前为止,代码按照我的意图工作,假设所有单元都适合可见窗口,但是当我在具有较小屏幕的设备上运行或者如果我也可以许多值(超过适合屏幕)然后不可见的单元格不计入TNU计算:

func updateIncomingData () {

    print ("Received String:")
    print (receivedDataString)
    print("Clearing TNU Array")
    TNUArray.removeAll()
    temperatureReadingArray = receivedDataString.components(separatedBy: " ")
    self.temperatureReadingsTable.reloadData()
    DispatchQueue.main.async {
        self.calculateTNU() //Need dispatch queue to run this AFTER it finished reloading data
    }
}

func calculateTNU()
{
    var TNU: Double = 0.000

    let minValue = TNUArray.min()
    let maxValue = TNUArray.max()
    print(TNUArray)
    if (minValue != nil && maxValue != nil)
    {
        TNU = maxValue! - minValue!
        calculatedTNU = String(format:"%.3f", TNU)
        TNULabel.text = calculatedTNU
    }
    else
    {
        print("Max or Min had nil values")
        TNULabel.text = "NA"
    }
}

我希望我很清楚,我会尝试按照一系列应该发生的事情来总结它:
- 接收蓝牙字符串(它包含由空格分隔的多个值)
- 将这些值解析为值数组,假设我得到9个值
- 使用拨动开关
为每个单元添加一个值 - 检查该单元的开关是否已切换,是否已将该值添加到数组以计算值之间的最大差值
- 计算最大最小值
- 显示值。

问题在于计算max-min值,因为它用于计算它的数组需要加载所有单元格,除非我滚动,否则不适合屏幕的单元格不会加载。登记/> 注意:我也尝试过使用DispatchQueue.main.async和reloadData(),但这并没有解决问题,因为它没有加载未显示的单元格

3 个答案:

答案 0 :(得分:0)

正如您已经注意到的,这里的问题是cellForRowAtIndexPath方法仅针对可见单元格调用,这就是为什么不计算屏幕外的单元格进行计算的原因。

我建议您创建一个类,您可以存储所有与蓝牙相关的对象,并且仅在您通过方法的完成处理程序得到通知时才进行计算。

为此,您可以创建一个单例来从项目中的任何位置访问您的Networking类,并确保您始终只有一个实例运行:

class Networking {

   static let shared = Networking()
}

现在创建一个数组来保存数据,并将updateIncomingData()函数放在那里,并为其添加一个完成处理程序,如下所示:

var temperatureReadingArray = [Int]()

func updateIncomingData(_ completion: @escaping () -> ()) {  
   // ..Do your networking/bluetooth operations here.       
   self?.temperatureReadingArray.append(newTemperature)       
   completion()
}

这允许您在某些新数据(newTemperature)即将到来时收到通知,以便您可以知道何时需要重新加载数据(无论单元格是否可见)

现在返回到您的Table View Controller和ViewDidLoad方法(或者您想要的任何地方,无论是按钮等),实现以下内容以通过完成处理程序进行通知,并异步重新加载表视图:

Networking.shared.loadMoreData { [weak self] in
    self?.tableView.reloadData()
    self?.doSomeCalculations()
}

另外不要忘记更新数据源,以便它反映数组中温度对象的数量:

// MARK: UITableViewDataSource

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {   
   return Networking.shared.temperatureReadingArray.count
}

最后,这里是指向我展示如何运作的项目的链接:

https://www.dropbox.com/s/lta464kl3ysilct/DisplayMinMax.zip?dl=0

如果您有任何疑问,请与我们联系!

<强>更新

以下是使用开关和闭包来获取单元格的开关状态的方法:

首先,您需要一个自定义表格视图单元格。在该自定义类中创建两个闭包,一个用于打开,一个用于关闭:

class CustomTableCell: UITableViewCell {

    var shouldToggleOn: ((CustomTableCell) -> Void)?
    var shouldToggleOff: ((CustomTableCell) -> Void)?

    // MARK: User Interaction

    @IBAction func handleSwitchValueChanged(_ sender: UISwitch) {

        if sender.isOn {

            shouldToggleOn?(self)

        } else {

            shouldToggleOff?(self)
        }

    }
}

现在让我们说你有一个Temperature对象,如下所示:

class Temperature : NSObject {

    // MARK: Properties

    var value: Int
    var boolean: Bool

    // MARK: Initializers

    init(value: Int, boolean: Bool) {
        self.value = value
        self.boolean = boolean
    }
}

在您的网络课程中,您可以创建一个阵列来保存所有温度:

var dataArray = [Temperature]()

每当调用loadMoreData时,您都可以将新的温度添加到数组中,如下所示:

dataArray.append(Temperature(value: randomNumber, boolean: false))

注意:您可以选择是否要将布尔值默认为true或false。

现在使用cellForRowAtIndexPath方法,现在可以使用您之前创建的两个闭包,并在用户切换开关ON或OFF时分别更新数据模型。

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

            // ...

            cell.shouldToggleOn = { (selectedCell) -> Void in
                print("turned ON")
                Networking.shared.dataArray[indexPath.row].boolean = true
            }

            cell.shouldToggleOff = { (selectedCell) -> Void in
                print("turned OFF...")
                Networking.shared.dataArray[indexPath.row].boolean = false
            }

            // ...
}

现在每当你收到一个新的温度时,你的loadMoreData方法的完成处理程序(我建议你不要放在你的cellForRow方法中,而是在你的viewDidLoad方法中)将被调用。

那时,您可以重新加载表格视图并开始计算:

Networking.shared.loadMoreData { [weak self] in
   self?.tableView.reloadData()
   self?.doSomeCalculations()
}

对于你的doSomeCalculations方法,只需过滤并映射以选择是否需要进行计算(即布尔值是ON还是OFF):

 func doSomeCalculations() {

      // consider only toggled on switches

      let onlyToggledOnArray = Networking.shared.dataArray.filter { $0.boolean == true }

      // get the min and max values

      let maxValue = onlyToggledOnArray.map{$0.value}.max()
      let minValue = onlyToggledOnArray.map{$0.value}.min()

      guard maxValue != nil, minValue != nil else {
         return
      }

      // ...
}

一切都应该正常工作:)

答案 1 :(得分:0)

只需使用数据模型类,并使用UITableView中填充的所有值初始化该类。

假设所有单元格都有切换开关,然后使用BOOL值(不同单元格的不同属性)初始化模型类,并将它们设置为默认值(是/否)

cellForRowAtIndexPath方法

if(modelClass.toggleValue)
  cell.toggleSwitch.setOn(true)
else
  cell.toggleSwitch.setOn(false)

每当您切换开关时,请更新模型类以获得所需的值。你完成了。现在对存储在模型类中的值进行计算。不是直接来自细胞。

希望这会对你有帮助..!

答案 2 :(得分:0)

谢谢你的建议。截至目前,我采用了一种更基本的方法,因为我还不太精明:
我创建了一个附加数组,用于保存每个单元格的索引,此数组对于要访问的所有其他视图控制器都是全局的。基本上我在初始化时将这个数组的数字添加到单元格中: 因为我永远不会收到超过9个频道,所以我做了这个:

var toggleSwitchStates:[Bool] = [true, true, true, true, true, true, true, true, true]

现在这是我的细胞的代码:

class TemperatureReading: UITableViewCell {

    var cellIndex: Int?
    @IBOutlet weak var channelLabel: UILabel!

    @IBOutlet weak var readingLabel: UILabel!

    @IBOutlet weak var toggleSwitch: UISwitch!

    @IBAction func switchWasToggled(_ sender: UISwitch) {
        print ("Switch toggled for: " + channelLabel.text!)
        changeArrayValue()
    }

    func changeArrayValue()
    {
        toggleSwitchStates[cellIndex!] = toggleSwitch.isOn
        NotificationCenter.default.post(name: Notification.Name(rawValue: TOGGLESWITCH_TOGGLED), object: nil) //Let the ViewController know there was a change, re-load TNU data
    } 

这就是我初始化它们的方式:

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

        //THIS CODE INITIALIZES EACH CELL, WITH ITS CORRESPONDING VALUES
        let cell = tableView.dequeueReusableCell(withIdentifier: temperatureReading) as! TemperatureReading

        cell.channelLabel.text = channelNames[indexPath.row]
        cell.readingLabel.text = temperatureReadingArray[indexPath.row]

        if (channelNames.count == 1)
        {
         cell.toggleSwitch.isHidden = true //IF there's only one channel, no need to calculate TNU
        }
        cell.toggleSwitch.isOn = toggleSwitchStates[indexPath.row]
        cell.cellIndex = chIndex[indexPath.row]

        return cell
    }

最后是贯穿两个数组的计算:

func calculateTNU()
    {
        TNUArray.removeAll()
        var TNU: Double = 0.000
        for (index, _) in temperatureReadingArray.enumerated() //First we populate the array based on the toggle switches
        {
            if (toggleSwitchStates[index] == true)
            {
                if let value = Double(temperatureReadingArray[index])
                {
                    TNUArray.append(value)
                    print("Appending value to array: \(value)")
                }
                else
                {
                    print("not a valid number")
                }
            }
            else
            {
                print("toggle switch is off")
            }
        } //Now we get max - min values
        let minValue = TNUArray.min()
        let maxValue = TNUArray.max()
        print("TNU Array is:\(TNUArray)")
        if (minValue != nil && maxValue != nil)
        {
            TNU = maxValue! - minValue!
            calculatedTNU = String(format:"%.3f", TNU) //3 decimal places
            TNULabel.text = calculatedTNU
        }
        else
        {
            print("Max or Min had nil values")
            TNULabel.text = "NA"
        }
    }