到目前为止的成功:我有一个远程数据源。数据被动态拉取到视图控制器中。该数据用于在每个可重用的自定义单元格上命名 .title 和 .subtitle。此外,每个自定义单元格都有一个 UISwitch,我已经能够获得用于发送推送通知的"订阅"信号(对于由单元格标题/副标题标识的给定组)和"取消订阅"信号的功能。
我剩下的一个问题:每当用户"重新访问"设置 VC,而我的代码正在"重置"UISwitches,它会导致 Xcode 9.2 中的以下警告:
- UISwitch.on 必须从主线程使用
- UISwitch.setOn(_:animated:) 必须仅从主线程使用
- -[UISwitch setOn:animated:notifyingVisualElement:] 必须从主线程使用
下面的代码"有效" - 但是所需的结果发生得相当慢(确实应该"打开"的UISwitches需要很长时间才能最终切换到"打开")。
更多详细信息:需要什么:每当显示或"重新显示"VC 时,如果用户订阅了给定组,我需要将自定义单元格的 UISwitch "重置"为"开",如果用户未订阅,则重置为"关闭"。理想情况下,每次显示VC时,应该使用OneSignal.getTags()函数伸出手触摸OneSignal服务器,并找出每个组的用户的"订阅状态"。我有这部分工作。此代码位于 VC 中。但是我需要以正确的方式进行操作,以适应有关线程的适当协议。
- VC 文件"ViewController_13_Settings.swift"包含带有可重用自定义单元格的表视图。 表视图
- 文件名为"自定义表视图单元格.swift">
- 自定义单元格称为"customCell"(我知道,我的名字都非常有创意)。
自定义单元格(在 XIB 中设计)内部只有三个项目:
- 标题 – 要订阅或取消订阅的"组"的显示"友好名称"。从远程数据源设置
- 副标题 – 上述组的隐藏"数据库名称"。对用户隐藏。从远程数据源设置。
- UISwitch - 名为"switchMinistryGroupList">
如何以编程方式正确设置 UISwitch?
以下是ViewController_13_Settings.swift中似乎相关的代码:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomTableViewCell
// set cell's title and subtitle
cell.textLabelMinistryGroupList?.text = MinistryGroupArray[indexPath.row]
cell.textHiddenUserTagName?.text = OneSignalUserTagArray[indexPath.row]
// set the custom cell's UISwitch.
OneSignal.getTags({ tags in
print("tags - (tags!)")
self.OneSignalUserTags = String(describing: tags)
print("OneSignalUserTags, from within the OneSignal func, = (self.OneSignalUserTags)")
if self.OneSignalUserTags.range(of: cell.textHiddenUserTagName.text!) != nil {
print("The (cell.textHiddenUserTagName.text!) UserTag exists for this device.")
cell.switchMinistryGroupList.isOn = true
} else {
cell.switchMinistryGroupList.isOn = false
}
}, onFailure: { error in
print("Error getting tags - (String(describing: error?.localizedDescription))")
// errorWithDomain - OneSignalError
// code - HTTP error code from the OneSignal server
// userInfo - JSON OneSignal responded with
})
viewWillAppear(true)
return cell
}
}
在 VC 代码的上一部分,这部分(如下)是功能正常的,但显然没有正确使用线程:
if OneSignalUserTags.range(of: cell.textHiddenUserTagName.text!) != nil {
print("The (cell.textHiddenUserTagName.text!) UserTag exists for this device.")
cell.switchMinistryGroupList.isOn = true
} else {
cell.switchMinistryGroupList.isOn = false
}
目前还不完全清楚你的代码在做什么,但似乎有一些事情需要整理,这将有助于你解决问题。
1) 改进对象的命名。这有助于其他人在提问时了解正在发生的事情。
不要称呼你的单元格CustomTableViewCell
- 称它为,比如说,MinistryCell
或代表其显示的数据的东西。而不是textLabelMinistryGroupList
和textHiddenUserTagName
树ministryGroup
和userTagName
等。
2)让细胞自己填充。将IBOutlets
放在单元格private
中,这样就无法直接在视图控制器中分配给它们。这是一个坏习惯!
3)创建一个对象(例如Ministry
)对应于您分配给单元格的数据。将其分配给单元格,并让单元格分配给其插座。
4)永远不要打电话给viewWillAppear
,或类似的东西!这些由系统调用。
你最终会得到这样的东西:
在视图控制器中
struct Ministry {
let group: String
let userTag: String
var tagExists: Bool?
}
您应该创建一个数组var ministries: [Ministry]
并在开始时填充它,而不是分别处理MinistryGroupArray
和OneSignalUserTagArray
。
在您的牢房中
class MinistryCell: UITableViewCell {
@IBOutlet private weak var ministryGroup: UILabel!
@IBOutlet private weak var userTagName: UILabel!
@IBOutlet private weak var switch: UISwitch!
var ministry: Ministry? {
didSet {
ministryGroup.text = ministry?.group
userTagName.text = ministry?.userTag
if let tagExists = ministry?.tagExists {
switch.isEnabled = false
switch.isOn = tagExists
} else {
// We don't know the current state - disable the switch?
switch.isEnabled = false
}
}
}
}
然后你的数据源方法将看起来像...
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! MinistryCell
let ministry = ministries[indexPath.row]
cell.ministry = ministry
if ministry.tagExists == nil {
OneSignal.getTags { tags in
// Success - so update the corresponding ministry.tagExists
// then reload the cell at this indexPath
}, onFailure: { error in
print("Error")
})
}
return cell
}