【问题标题】:Could not cast value of type无法转换类型的值
【发布时间】:2016-01-22 05:02:54
【问题描述】:

我正在创建一个井字游戏应用程序。当游戏重置时,所有 Xs 和 Os UIButton 图像应返回到 nil。棋盘中的每个空格都标记为 0 到 8。

  var buttonsToClear : UIButton

    for var i = 0; i < 9; i++ {

        buttonsToClear = view.viewWithTag(i) as! UIButton

        buttonsToClear.setImage(nil, forState: .Normal)

    }

在我添加导航栏之前一切正常。现在我收到错误消息

“无法将 '_UINavigationBarBackground' (0x107e2f2a0) 类型的值 > 转换为 'UIButton' (0x107e357e0)。 (lldb)

我绝对确定我的新导航栏的标签是“10”。

还有什么可能触发此消息?

【问题讨论】:

  • 导致崩溃的不是导航栏本身,而是导航栏背景,这是UINavigationBar的私有属性
  • 尝试在循环顶部打印print("i = \(i): \(view.viewWithTag(i)?.description ?? "")"),只是为了查看viewWithTag 显然返回非按钮视图的标签值和视图对象。此外,可以在viewDidLoad 中以编程方式为 UIButtons 和导航栏设置标签值。
  • 另外,考虑有条件的展开:if let buttonsToClear = view.viewWithTag(i) as? UIButton { .... },并且仅当它是一个按钮时才设置图像。这是一种很好的做法,更安全,如果您更改标签,您将永远不会尝试将 setImage 设置为某个东西而不是按钮。

标签: ios swift cocoa-touch


【解决方案1】:

您的逻辑是正确的,您的代码将完美运行。

你得到了错误

“无法转换类型 '_UINavigationBarBackground' 的值 (0x107e2f2a0)>到“UIButton”(0x107e357e0)。 (lldb)

因为,当您的 for loop 运行时,第一个索引将为零,即 i = 0,

buttonsToClear = view.viewWithTag(i) as! UIButton

会变成

buttonsToClear = view.viewWithTag(0) as! UIButton

因此,在这种情况下,您尝试获取 tag 值为 0 的视图。

UINavigationBarBackground 也有标签值 0。

注意:

1.当系统发现两个具有相同标签值的视图,并且您尝试使用viewWithTag: 获取视图时,它将返回第一个视图。

2。每个视图都有默认标记值 0。

3.只需在 storyboard 或 XIB 中拖拽 UILabel 或 UIView 并检查其标签值。

因此,在您的情况下,它将返回 UINavigationBarBackground 而不是 UIButton。所以你不能将UINavigationBarBackground 类型转换为UIButton。

您的错误的解决方案:

永远不要给从 0 开始的标签。 尝试给标签以 1000 之类的值开头。 所以你的第一个按钮将有标签 1000,下一个将有 1001,然后是 1002,依此类推。

如下编写你的 for 循环。

var buttonsToClear : UIButton

    for var i = 1000; i < 1009; i++ {

        buttonsToClear = view.viewWithTag(i) as! UIButton

        buttonsToClear.setImage(nil, forState: .Normal)

    }

【讨论】:

  • 我仍然不会通过使用as! 将UIView 强制转换为UIButton,我会先使用as? 来检查类型。
【解决方案2】:

你不应该使用 0 标签,因为 0 是默认值,你可以得到任何视图(在这种情况下是导航的背景)。

从 1 开始标记视图。

【讨论】:

    【解决方案3】:

    您必须能够看到堆栈跟踪才能了解触发的位置?无论如何,显而易见的解决方案是在调试器中运行它,打开异常断点并查看发生了什么。

    正如 Jose 所说,不要使用标记 0。

    【讨论】:

      【解决方案4】:

      您需要一种不依赖于仅使用标签来识别您的子视图的新方法


      问题是,您的循环遇到了UINavigationBar、_UINavigationBarBackground 上的私有属性。因为你不能在上面设置标签,所以它的默认标签是0。因此,您将尝试向其发送UIButton 函数。

      我会简单地遍历您的子视图并检查视图是否为UIButton 类型,然后然后检查它的标签(确保仍然不要使用标签0,因为那是默认标签),然后再删除它。 这样您就可以 100% 确定您获得了正确的子视图。

      var buttonsToClear : UIButton
      
      for view in view.subviews { // Loop through subviews
          if let button = view as? UIButton { // Check if UIButton
              if button.tag > 0 || button.tag < 10 { // Check tags
                  buttonsToClear = button
                  buttonsToClear.setImage(nil, forState: .Normal)
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        导航栏中的按钮是UIBarButtonItem 而不是UIButton,而且看起来您的标记视图可能是UINavigationBar,而不是UIBarButtonItem。

        我建议不要使用标签,如果您要使用标签,请使用常量而不是幻数。将IBOutlet 添加到您希望更改的视图中,而不是标签。如果您要清除的按钮数量较多,可以使用IBOutletCollection,但您会再次遇到导航栏中的按钮是UIBarButtonItem 而不是UIButton 的问题。

        您可以将leftBarButtonItem 设置为UIBarButtonItem,并使用是UIButton 的自定义视图,但是当您可以更改UIBarButtonItem 本身时,这似乎很迂回!

        【讨论】:

        • 如错误消息所示,有一个名为 _UINavigationBarBackground 的私​​有 UIKit 类(以及许多其他视图)的默认标记值为 0。viewWithTag 只是返回它发生的第一个视图使用默认标记值定位。
        • 是的,你是对的,没有注意到他在使用标签 0。我的观点仍然是它不是 UIButton,并且使用幻数作为标签是不好的做法! :)
        猜你喜欢
        • 2018-11-02
        • 2016-03-05
        • 2023-03-20
        • 2015-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多