在 Switch Case 中声明 UI 个对象

Declaring UI Objects in Switch Cases

我已经阅读了 switch cases 的范围,jump labels 等等,但是这里建议的解决方案似乎暗示添加花括号可以解决这个问题.但是,这似乎仍然不起作用:

switch (objectType) {

  case label:   //label is an integer constant
    NSLog(@"statement before declaration");
    UILabel *control = [[UILabel alloc] init];       //no error
    break;

  case button:  //button is an integer constant
    {
      UIButton *control = [[UIButton alloc] init];   //no error
    }
    break;    

  default:
    break;
}

// error when trying to use the *control* variable,
// Use of undeclared identifier 'control'

有什么方法可以用 switch 语句来实现吗?


2015 年 5 月 23 日:尝试了很多不同的方法都没有成功。

编辑:实施 Cyrille 建议的解决方案时出错:

UIView *control = nil;
switch (objectType)
{
  case label:  //button is an integer constant
    {
      control = [[UILabel alloc] init];   //no error

      //error: property 'text' not found on object of type 'UIView *'
      control.text = @"Something...";     
    }
    break; 

  default:
    break;
}

显然,即使在从 UIView 重铸为 UILabel 之后,该对象也没有继承 的所有属性UILabel,因此错误:

属性 'text' 未在 'UIView *'

类型的对象上找到

甚至 Zil 的前缀类型 (UILabel*)[[UILabel alloc]init]; 的建议也没有奏效。

有没有人让它工作?


DUNCAN C 的解决方案 (请参阅下面接受的答案)

    UIView *control = nil;
    switch (objectType)
    {
        case label:  
            control = [[UILabel alloc] init];
            ((UILabel *)control).text = @"Something...";     
            break; 

        default:
            break;
    }

    // UI object instantiated inside switch case recognised!
    ((UILabel *)control).textColor = [UIColor redColor];

谢谢 Duncan C.

是的,只需在切换之前声明一个通用 control

UIView *control = nil;
switch (objectType) {

  case label:   //label is an integer constant
    NSLog(@"statement before declaration");
    control = [[UILabel alloc] init];       //no error
    break;

  case button:  //button is an integer constant
    {
      control = [[UIButton alloc] init];   //no error
    }
    break; 

  case other:
    control = [[UIView alloc] init]; // works too   

  default:
    break;
}

NSLog(@"Control frame is %@", NSStringFromCGRect(control.frame));

问题是您的 control 变量是在 switch 语句的范围内声明的,然后您试图在外部使用它:

UILabel *controlLabel = nil;
UIButton *controlButton = nil;

    /*
    I've defined two variables but you could do the same with one like so
    id control = nil;
    or
    UIControl *control = nil;
    */

switch (objectType) {

  case label:   //label is an integer constant
    NSLog(@"statement before declaration");
    controlLabel = [[UILabel alloc] init];       //no error
    break;

  case button:  //button is an integer constant
    {
      controlButton = [[UIButton alloc] init];   //no error
    }
    break;    

  default:
    break;
}

if (controlLabel) {

} else if (controlButton) {

}

Objective-C 中的范围规则非常简单。任何时候输入一对大括号,都会进入一个新的范围级别。在该范围内声明的任何变量仅存在于该范围内。当你退出大括号时,变量不复存在。

switch 中的个别情况不需要大括号,因此它们不定义局部范围级别,但整个 switch 语句 使用大括号,等等它确实定义了一个本地范围。在 switch 语句的大括号内声明的任何变量在大括号外都不存在。

当您进入新的范围级别时,您可以访问在您的 parent 范围内声明的所有内容。

你没有说清楚你想做什么。我 THINK 你想要做的是有一个 parent class 的变量,然后把来自 subclasses 的新 objects 放入其中,并且仍然可以访问 child class.

的独特属性

如果您声明一个基数 class 的变量,您可以将作为该类型的子class 的任何 object 分配给该变量。

如果要访问子class的属性,则必须将变量转换为适当的类型:

UIView *control = nil;
switch (objectType)
{
  case label:  
    control = [[UILabel alloc] init];   //no error

    ((UILabel *)control).text = @"Something...";     
    break; 
  case button: 
    control = [UIButton buttonWithType: UIButtonTypeSystem];
    [((UIButton *)control) setTitle: "Foo" forState: UIControlStateNormal];
  default:
    break;
}

编辑

我发现转换语法很难打字,而且会让人感到困惑。有时候创建一个新的object实际类型的临时变量,做任何你需要做的事情,然后把它赋值给目标变量会更清楚,像这样:

UIView *control = nil;
switch (objectType)
{
  case label:  
    UILabel *aLabel = [[UILabel alloc] init];   //no error

    aLabel.text = @"Something...";   
    aLabel.someOtherProperty = someOtherValue;
    control = aLabel;  
    break; 
  case button: 
    UIButton aButton = [UIButton buttonWithType: UIButtonTypeSystem];
    [aButton setTitle: "Foo" forState: UIControlStateNormal];
    control = aButton
  default:
    break;
}

在switch语句之外,你不会知道object控件变量包含的是哪种类型,所以你将只能访问UIView基类的属性class,除非您将变量转换为特定类型,如上所示。

(请注意,上面的名称 "control" 是一个错误的选择,因为标签不是控件。最好使用像 "aView" 这样的名称,它暗示了常见的 class变量中可能存在的所有 object。)

将控件声明为 UIView * 后,如果您尝试访问 UIView 上不存在的属性,则会出现错误。

然而,正如 Duncan C 所说,您始终可以使用点符号来转换和访问 属性,例如:

((UILabel *)control).text = @"This Works";

然后访问值

NSLog(@"Control text: %@", ((UILabel *)control).text);

我打算采用不同的方法,将您的控件声明为 id 并使用 Objective-C 方法调用。您需要确保控件响应您尝试调用的选择器。如果您想在尝试访问选择器时保护您的应用程序不会崩溃,您可以使用 respondsToSelector: 方法。 我的建议的一个例子:

unsigned objectType = 1;
id control = nil;

switch (objectType) {

    case 1:   //label is an integer constant
        NSLog(@"statement before declaration");
        control = [[UILabel alloc] init];       //no error
        [control setText:@"a"];
        break;

    case 2:  //button is an integer constant
    {
        control = [[UIButton alloc] init];   //no error
    }
        break;

    default:
        break;
}

NSLog(@"Control text %@", [control text]);

结果是:

2015-05-23 13:12:06.840 test2[46589:12231454] statement before declaration
2015-05-23 13:12:06.841 test2[46589:12231454] Control text `a`

希望对您有所帮助:)