2.3 UIView嵌套

2.3.1 追加子元素

UIView中拥有addSubView:这样的实例方法,可以在UIView中追加UIView作为子元素。当然UILabel及UIButton等UIView的子类也可以使用addSubView:方法自由地追加。这里首先看一个实例,在UIButton中使用addSubView:方法追加子元素的UIButton。

// 追加1-1按钮
UIButton* button11 = [UIButton buttonWithType:UIButtonTypeRounde dRect];
button11.frame = CGRectMake(10,10,300,300);
[button11 setTitle:@”1-1” forState:UIControlStateNormal];
[button11 addTarget:self
             action:@selector(button11DidPush:)
    forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button11];
// 追加1-1-1按钮
UIButton* button111 = [UIButton buttonWithType:UIButtonTypeRound edRect];
button111.frame = CGRectMake(20,20,260,100);
[button111 setTitle:@”1-1-1” forState:UIControlStateNormal];
[button111 addTarget:self
              action:@selector(button111DidPush:)
    forControlEvents:UIControlEventTouchUpInside];
[button11 addSubview:button111];
// 追加1-1-2按钮
UIButton* button112 = [UIButton buttonWithType:UIButtonTypeRound edRect];
button112.frame = CGRectMake(20,180,260,100);
[button112 setTitle:@”1-1-2” forState:UIControlStateNormal];
[button112 addTarget:self
              action:@selector(button112DidPush:)
    forControlEvents:UIControlEventTouchUpInside];
[button11 addSubview:button112];
// 追加1-1-2-1按钮
UIButton* button1121 = [UIButton buttonWithType:UIButtonTypeRoun dedRect];
button1121.frame = CGRectMake(10,10,95,80);
[button1121 setTitle:@”1-1-2-1” forState:UIControlStateNormal];
[button1121 addTarget:self
               action:@selector(button1121DidPush:)
    forControlEvents:UIControlEventTouchUpInside];
[button112 addSubview:button1121];
// 追加1-1-2-2按钮
UIButton* button1122 = [UIButton buttonWithType:UIButtonTypeRoun dedRect];
button1122.frame = CGRectMake(155,10,95,80);
[button1122 setTitle:@”1-1-2-2” forState:UIControlStateNormal];
[button1122 addTarget:self
               action:@selector(button1122DidPush:)
    forControlEvents:UIControlEventTouchUpInside];
[button112 addSubview:button1122];

此段实例代码的执行结果如图2-10所示。

图2-10 addSubView:方法实例

另外,在UIView中还有superview属性subviews属性。通过superview属性可以取得追加自己的UIView(父元素),而subviews可以以数组NSArray的形式取得追加到自己之下的所有UIView(子元素)。单击图2-10中的任意按钮,显示其父元素以及子元素列表的代码如下所示。以下实例代码是紧接着上面实例代码的。

-(void)button11DidPush:(id)sender {
  [self alertMessage:sender];
}
-(void)button111DidPush:(id)sender {
  [self alertMessage:sender];
}
-(void)button112DidPush:(id)sender {
  [self alertMessage:sender];
}
-(void)button1121DidPush:(id)sender {
  [self alertMessage:sender];
}
-(void)button1122DidPush:(id)sender {
  [self alertMessage:sender];
}
-(void)alertMessage:(UIButton*)button {
  // 显示self的标题作为警告框的标题
  NSString* title = [NSString stringWithFormat:@"self = %@",button.titleLabel.text];
  // 取得superview的标题
  // 但是当superview为非UIButton的情况下,以“UIViewController”替代
  NSString* superViewName;
  if([button.superview isKindOfClass:[UIButton class]]){
    superViewName =((UIButton*)button.superview).titleLabel.text;
  } else {
    superViewName = @"UIViewController";
  }
  // 取得subviews的标题
  NSMutableString* subviews = [[[NSMutableString alloc] initWithCapacity:64] autorelease];
  [subviews setString:@""];
  for(id view in button.subviews){
    NSString* addString;
    if([view isKindOfClass:[UIButton class]]){
    // 如果子元素为UIButton时,取titleLabel的text属性值
      addString =((UIButton*)view).titleLabel.text;
    } else if([view isKindOfClass:[UILabel class]]){
    // 如果为UILabel时取其text属性值
      addString =((UILabel*)view).text;
    } else {
    // 上述以外的情况
      addString = [view description];
    }
    if([subviews length] > 0){
      [subviews appendString:@","];
    }
    [subviews appendString:addString];
  }
  NSString* message = [NSString stringWithFormat:@"superview =%@\r\nsubviews = %@",superViewName,subviews];
  UIAlertView* alert = [[[UIAlertView alloc] initWithTitle:title message:message
                 delegate:nil
                 cancelButtonTitle:nil
                 otherButtonTitles:@”OK”,nil ] autorelease];
  [alert show];}

例如,当触摸[1-1-2]时,将显示如图2-11所示的警告框。

图2-11 显示superview与subviews的信息

此时,[1-1-2]的父元素为[1-1],子元素为[1-1-2]与[1-1-2-1]及[1-1-2-2]。之所以[1-1-2]会包含于其中而作为UIButton的子元素,是因为管理自身标题的UILabel也是其子元素之一。

2.3.2 子元素的插入与删除

上一小节我们介绍了如何使用addSubview:方法进行子元素的追加。实际上,追加子元素的方法除 addSubView:之外,还有insertSubview:atIndex:方法insertSubview:aboveSubview:方法以及insertSubview:belowSubview:方法。另外,从父元素中删除特定的子元素时可使用removeFromSuperview:方法

除上述方法外,用于管理UIView子元素状态的方法还有:交换两个子元素顺序的exchangeSubviewAtIndex:withSubviewAtIndex:方法以及检查是否为某元素的子元素的isDescendantOfView:方法

上述六个方法使用实例代码如下所示。

// 追加父标签
parent_ = [[UILabel alloc] initWithFrame:CGRectMake(0,0,320,320)];
parent_.textAlignment = UITextAlignmentCenter;
parent_.text = @"PARENT";
[self.view addSubview:parent_];
// 追加1个子标签
UILabel* child3 = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
child3.text = @"CHILD 3";
[child3 sizeToFit];
[parent_ insertSubview:child3 atIndex:0];
// 在上一个标签CHILD 3 下插入 CHILD 1
UILabel* child1 = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
child1.text = @"CHILD 1";
[child1 sizeToFit];
[parent_ insertSubview:child1 belowSubview:child3];
// 在CHILD 1 上追加 CHILD 2
UILabel* child2 = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
child2.text = @"CHILD 2";
[child2 sizeToFit];
[parent_ insertSubview:child2 aboveSubview:child1];
// 让CHILD 1 与 CHILD 3 交换
[parent_ exchangeSubviewAtIndex:0 withSubviewAtIndex:2];
// 如果CHILD 3 为 PARENT 子元素的话
if([child3 isDescendantOfView:parent_]){
// 删除CHILD 3
[child3 removeFromSuperview];
}

2.3.3 UIView的靠前显示与退后隐藏

多个UIView追加到同一画面上时,此时肯定会出现其中几个相互重叠的情况。这种情况下,我们可以调用bringSubviewToFront:方法将特定的UIView放置在前方,也可以调用sendSubviewToBack:方法将特定的UIView移动到后方。例如,我们在画面上追加了labelA_与labelB_两个标签,依次追加 labelA_与labelB_的代码如下。

[self.view addSubview:labelA_];
[self.view addSubview:labelB_];

画面显示的效果如图2-12所示。

图2-12 重叠的UIView

labelB_是后面追加进来的,因此labelB_必然是在前方显示。此时如果想让labelA_标签在前方显示,可以调用bringSubviewToFront:方法,代码如下。

[self.view bringSubviewToFront:labelA_];

执行后显示的效果如图2-13所示。

图2-13 将A标签置前

相反,如果我们再次想让labelA_移动到后方显示时,可调用sendSubviewToBack:方法,代码如下。

[self.view sendSubviewToBack:labelA_];

此时将画面返回如图2-13所示的状态。

2.3.4 附加标签(tag)及UIView的检索

UIView中定义有tag属性。在此tag属性中开发人员可以自行设置数值。但是就算设置了此属性值也不会影响程序本身的运行。tag属性通常只是被开发人员作为标记来使用。明确设置了tag属性的UIView可以使用viewWithTag:方法很简单地检索到,以下是tag属性的使用实例,注意此处仅罗列了主要的代码。

-(void)viewDidLoad {
  [super viewDidLoad];
  // 背景设置成黑色
  self.view.backgroundColor = [UIColor blackColor];
  // 追加父标签
  parent_ = [[UILabel alloc] initWithFrame:CGRectMake(0,0,320,320)];
  parent_.textAlignment = UITextAlignmentCenter;
  parent_.text = @"PARENT";
  [self.view addSubview:parent_];
  // 追加10个子标签
  for(int i = 1;i <= 10;++i){
     UILabel* child = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
    child.text = [NSString stringWithFormat:@"CHILD %d",i];[child sizeToFit];
    CGPoint newPoint = child.center;newPoint.y += 30 * i;
    child.center = newPoint;
    [parent_ addSubview:child];
    // 将第8个标签的tag设置成999
    if(8 == i){
      child.tag = 999;
    }
  }
  // 追加search按钮
  UIButton* searchButton = [UIButton buttonWithType:UIButtonTypeRo undedRect];
  searchButton.frame = CGRectMake(0,0,150,40);
  CGPoint newPoint = self.view.center;
  newPoint.y = self.view.frame.size.height - 40;
  searchButton.center = newPoint;
   [searchButton setTitle:@"search 999" forState:UIControlState Normal];
  [searchButton addTarget:self
                   action:@selector(searchDidPush)
         forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:searchButton];
}
#pragma mark ----- Private Methods -----
-(void)searchDidPush {
  NSString* message;
  // 从parent_的子元素中检索tag为999的元素,找到后显示警告框
  UILabel* label =(UILabel*)[parent_ viewWithTag:999];
  if(label){
    message = label.text;
  } else {
    message = @"nothing";
  }
  UIAlertView* alert = [[[UIAlertView alloc] initWithTitle:@"search 999"
                                      message:message
                                      delegate:nil
                            cancelButtonTitle:nil
                            otherButtonTitles:@"OK",nil ] autorelease];
  [alert show];
}