我遇到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(),但这并没有解决问题,因为它没有加载未显示的单元格
答案 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"
}
}