需要帮助在自定义单元格(XIB、Swift 4、Xcode 9)中设置 UISwitch



到目前为止的成功:我有一个远程数据源。数据被动态拉取到视图控制器中。该数据用于在每个可重用的自定义单元格上命名 .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 中设计)内部只有三个项目:

  1. 标题 – 要订阅或取消订阅的"组"的显示"友好名称"。从远程数据源设置
  2. 副标题 – 上述组的隐藏"数据库名称"。对用户隐藏。从远程数据源设置。
  3. 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或代表其显示的数据的东西。而不是textLabelMinistryGroupListtextHiddenUserTagNameministryGroupuserTagName等。

2)让细胞自己填充。将IBOutlets放在单元格private中,这样就无法直接在视图控制器中分配给它们。这是一个坏习惯!

3)创建一个对象(例如Ministry)对应于您分配给单元格的数据。将其分配给单元格,并让单元格分配给其插座。

4)永远不要打电话给viewWillAppear,或类似的东西!这些由系统调用。

你最终会得到这样的东西:

在视图控制器中

struct Ministry {
let group: String
let userTag: String
var tagExists: Bool?
}

您应该创建一个数组var ministries: [Ministry]并在开始时填充它,而不是分别处理MinistryGroupArrayOneSignalUserTagArray

在您的牢房中

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
}

最新更新