无法将类型为的值强制转换为目标类型

4
我正在创建一个井字棋应用程序。当游戏重置时,所有的 X 和 O 的 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)

    }

在添加导航栏之前,一切都很正常。现在我收到以下错误消息:

"Could not cast value of type '_UINavigationBarBackground' (0x107e2f2a0) >to 'UIButton' (0x107e357e0). (lldb)

我非常确定我的新导航栏具有标签"10"。

Navigation Bar Tag

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


导致崩溃的不是导航栏本身,而是导航栏背景,它是 UINavigationBar 的私有属性。 - Hamish
尝试在循环顶部打印print("i = \(i): \(view.viewWithTag(i)?.description ?? "")"),以查看标签值和视图对象,以便确定viewWithTag返回的是否为非按钮视图。此外,您还可以在viewDidLoad中将标记值设置为UIButtons和您的导航栏。 - dfrib
1
此外,考虑使用条件解包:if let buttonsToClear = view.viewWithTag(i) as? UIButton { .... },并且仅在它是一个按钮时设置图像。这是一种良好的实践,更加安全,如果您更改标签,您永远不会尝试将setImage设置为除按钮以外的其他内容。 - jrf
4个回答

4

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

但是你遇到了错误:

"Could not cast value of type '_UINavigationBarBackground' (0x107e2f2a0) >to 'UIButton' (0x107e357e0). (lldb)

这是因为当你的for循环运行时,第一个索引将是零,即i = 0,

buttonsToClear = view.viewWithTag(i) as! UIButton

将变成

buttonsToClear = view.viewWithTag(0) as! UIButton

在这种情况下,您正在尝试获取标记值为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?检查类型。 - Hamish

4

请勿使用0标签,因为0是默认值,您可以获得任何视图(在这种情况下是导航栏的背景)。

从1开始标记视图。


1

你必须能够查看堆栈跟踪以查看触发点在哪里?无论如何,明显的解决方案是在调试器中运行此代码,打开异常断点并查看发生了什么。

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


0

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


问题在于,您的循环遇到了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)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接