需要帮助在自定义单元格中设置 UISwitch (XIB, Swift 4, Xcode 9)
Need help setting UISwitch in custom cell (XIB, Swift 4, Xcode 9)
到目前为止的成功: 我有一个远程数据源。数据被动态拉入视图控制器。该数据用于在每个可重用自定义单元格上命名 .title 和 .subtitle。此外,每个自定义单元格都有一个 UISwitch,我已经能够使用它发送推送通知的“订阅”信号(对于由单元格的 title/subtitle 标识的给定组)和“取消订阅”信号以及。
我剩下的一个问题:每当用户 "revisits" 设置 VC,而我的代码是 "resetting" UISwitches 时,它会导致Xcode 9.2 中的以下警告:
- UISwitch.on 必须从主线程使用
- UISwitch.setOn(_:animated:) 只能在主线程中使用
- -[UISwitch setOn:animated:notifyingVisualElement:] 必须从主线程使用
"works" 下面的代码——但是期望的结果发生得相当慢(确实应该是 "on" 的 UISwitch 需要一段时间才能最终翻转到 "on") .
更多详情:
需要什么:每当显示 VC 或 "re-shown," 我需要 "reset" 自定义单元格的 UISwitch 到 "on" 如果用户订阅了给定的组,并且"off" 如果用户未订阅。理想情况下,每次显示 VC 时,应该有什么东西伸出并触摸 OneSignal 服务器,并使用 OneSignal.getTags()函数。我有那部分工作。此代码在 VC 中。但我需要以正确的方式进行,以适应有关线程的适当协议。
- VC 文件,“ViewController_13_Settings.swift”包含一个 Table 带有可重用自定义单元格的视图。
- Table 查看文件名为"CustomTableViewCell.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) 让单元格自行填充。在您的单元格 private
中创建您的 IBOutlets
这样您就不能直接在您的视图控制器中分配给它们。这是个坏习惯!
3) 创建一个对象(例如,Ministry
),它对应于您要分配给单元格的数据。把这个赋给cell,让cell赋给它的Outlets。
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
}
}
}
}
那么你的 dataSource 方法看起来像...
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
}
到目前为止的成功: 我有一个远程数据源。数据被动态拉入视图控制器。该数据用于在每个可重用自定义单元格上命名 .title 和 .subtitle。此外,每个自定义单元格都有一个 UISwitch,我已经能够使用它发送推送通知的“订阅”信号(对于由单元格的 title/subtitle 标识的给定组)和“取消订阅”信号以及。
我剩下的一个问题:每当用户 "revisits" 设置 VC,而我的代码是 "resetting" UISwitches 时,它会导致Xcode 9.2 中的以下警告:
- UISwitch.on 必须从主线程使用
- UISwitch.setOn(_:animated:) 只能在主线程中使用
- -[UISwitch setOn:animated:notifyingVisualElement:] 必须从主线程使用
"works" 下面的代码——但是期望的结果发生得相当慢(确实应该是 "on" 的 UISwitch 需要一段时间才能最终翻转到 "on") .
更多详情: 需要什么:每当显示 VC 或 "re-shown," 我需要 "reset" 自定义单元格的 UISwitch 到 "on" 如果用户订阅了给定的组,并且"off" 如果用户未订阅。理想情况下,每次显示 VC 时,应该有什么东西伸出并触摸 OneSignal 服务器,并使用 OneSignal.getTags()函数。我有那部分工作。此代码在 VC 中。但我需要以正确的方式进行,以适应有关线程的适当协议。
- VC 文件,“ViewController_13_Settings.swift”包含一个 Table 带有可重用自定义单元格的视图。
- Table 查看文件名为"CustomTableViewCell.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) 让单元格自行填充。在您的单元格 private
中创建您的 IBOutlets
这样您就不能直接在您的视图控制器中分配给它们。这是个坏习惯!
3) 创建一个对象(例如,Ministry
),它对应于您要分配给单元格的数据。把这个赋给cell,让cell赋给它的Outlets。
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
}
}
}
}
那么你的 dataSource 方法看起来像...
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
}