【问题标题】:Need help setting UISwitch in custom cell (XIB, Swift 4, Xcode 9)需要帮助在自定义单元格中设置 UISwitch(XIB、Swift 4、Xcode 9)
【发布时间】:2018-04-26 06:41:56
【问题描述】:

到目前为止的成功:我有一个远程数据源。数据被动态拉入视图控制器。该数据用于在每个可重用的自定义单元格上命名 .title 和 .subtitle。此外,每个自定义单元格都有一个 UISwitch,我已经能够使用它来发送推送通知的“订阅”信号(对于由单元格的标题/副标题标识的给定组)和“取消订阅”信号以及.

我剩下的一个问题:每当用户“重新访问”设置 VC,而我的代码正在“重置” UISwitches 时,它会在 Xcode 9.2 中导致以下警告:

  • UISwitch.on 必须在主线程中使用
  • UISwitch.setOn(_:animated:) 只能在主线程中使用
  • -[UISwitch setOn:animated:notifyingVisualElement:] 必须在主线程中使用

下面的代码“工作”——但是所需的结果发生得相当慢(确实应该“打开”的 UISwitches 需要很长时间才能最终切换到“打开”)。

更多详情: 需要什么:无论何时显示或“重新显示”VC,如果用户订阅了给定的组,我需要将自定义单元格的 UISwitch“重置”为“on”,如果用户订阅了该组,则“重置”为“off”未订阅。理想情况下,每次显示 VC 时,都应该使用 OneSignal.getTags() 函数访问 OneSignal 服务器并找出用户对每个组的“订阅状态”。我有那部分工作。此代码在 VC 中。但我需要以正确的方式进行操作,以适应有关线程的正确协议。

  • VC 文件,“ViewController_13_Settings.swift”包含一个带有可重用自定义单元格的表格视图。
  • 表格视图文件名为“CustomTableViewCell.swift”
  • 自定义单元格称为“customCell”(我知道,我的名字都很有创意)。

自定义单元格(在 XIB 中设计)只有三个项目:

  1. 标题 – 显示的“组”的“友好名称”,要订阅或取消订阅。从远程数据源设置
  2. Subtitle – 上述组的隐藏“数据库名称”。对用户隐藏。从远程数据源设置。
  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
    }

【问题讨论】:

    标签: ios swift xcode uiswitch


    【解决方案1】:

    目前尚不完全清楚您的代码在做什么,但似乎有一些事情需要整理,这将帮助您解决问题。

    1) 改进对象的命名。这有助于其他人在提问时了解正在发生的事情。

    不要称您的单元格为CustomTableViewCell - 称它为MinistryCell 或代表其显示数据的名称。而不是textLabelMinistryGroupListtextHiddenUserTagNameministryGroupuserTagName 等。

    2) 让单元格自行填充。在您的单元格private 中创建您的IBOutlets,这样您就不能直接在您的视图控制器中分配给它们。这是个坏习惯!

    3) 创建一个对象(例如Ministry),它对应于您分配给单元格的数据。将此分配给单元格,然后让单元格分配给它的 Outlets。

    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
                }
            }
        }
    }
    

    那么你的 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
    }
    

    【讨论】:

    • 谢谢!当您发布您的答案时,我正在编辑我的问题以试图使其更清晰,并解决一些代码无法运行的事实,因为我将其移到 OneSignal.getTags 函数之外。我会仔细研究你的答案并报告回来。与此同时,我希望我修改后的问题更有意义。很快就会有更多。
    • 为了清楚起见,当你写下,“在你的视图控制器中... ) IBOutlet private weak var switch: UISwitch!` 这些项目是我之前“控制拖动”以将它们连接到与 XIB 关联的表视图 (swift) 文件的项目,但您说它们应该是“控制拖动” " 将它们连接到主 VC 的 swift 文件,对吗?
    • 对不起,这是我的错误,应该在你的表格视图单元子类中。您可以控制从情节提要中的单元格拖动到单元格的类
    • 再次感谢!我投票赞成你的答案,虽然我是新人,而且我的声誉分数太低,以至于我的投票无法显示。您的答案和代码向我介绍了几个对我来说新的概念,并导致了很多阅读,很多小时尝试不同的代码配置,并取得了很大的进步。经过很多努力,我仍然受到一些事情的阻碍。 1)虽然在第一次查看时单元格的开关显示正确,但在任何重新访问时它们都显示为关闭 - 并且 ViewWillAppear 中的 reloadData() 没有帮助 2)我有协议 w 委托工作,但我仍然得到“UILabel.text只能在主线程中使用。”
    • 更新:我一次又一次地回到你的答案,但是在学习了很多错误和一些成功之后,我又回去检查了一次,发现我尝试了其他方法(虽然到处乱窜),当我重置特定措辞以匹配您的答案时,重新访问时未设置开关的问题就消失了!我很高兴能在这方面取得成功。关于主线程的警告是紫色的,现在似乎只是一种威胁。我的应用程序按预期工作!非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-30
    • 1970-01-01
    • 1970-01-01
    • 2016-11-20
    • 2017-01-20
    相关资源
    最近更新 更多