2.5 UIView的状态监视

UIView中,事先定义了监视UIView状态变化的方法。当创建UIView的子类时,重写这些状态监视方法,可以实现在状态变化的时刻进行各种处理。表2-9中罗列出了UIView的状态监视方法以及各自调用的时机。

表2-9 UIView状态监视方法列表

重写这些方法实现具体处理的实例代码如下。

// 定义UILabel(UIView)的子类
@interface NewLabel :UILabel
@end
// 实现NewLabel 、重写UIView的状态监视方法
@implementation NewLabel
-(void)didAddSubview:(UIView*)subview {
  NSLog(@"didAddSubview");
}
-(void)didMoveToSuperview {
  NSLog(@"didMoveToSuperview");
}
-(void)didMoveToWindow {
  NSLog(@"didMoveToWindow");
}
-(void)willMoveToSuperview:(UIView*)newSuperview {
  NSLog(@"willMoveToSuperview");
}
-(void)willMoveToWindow:(UIWindow*)newWindow {
  NSLog(@"willMoveToWindow");
}
-(void)willRemoveSubview:(UIView*)subview {
  NSLog(@"willRemoveSubview");
}
@end

例如,将此NewLabel作为子元素追加到具体的UIView后,willMoveToSuperview:方法以及didMoveToSuperview 方法将被依次调用,我们可以通过日志具体查看调用过程,日志内容如下。

……
willMoveToSuperview
willMoveToWindow
didMoveToWindow
didMoveToSuperview
……

知识专栏(Column):frame与bounds的区别

我们改变UIView的位置及尺寸时会使用到frame属性。但是,容易引起混淆的是,还有另外一个决定UIView尺寸的bounds属性。实际上,当我们向bounds属性中设置新的CGRect时,UIView的尺寸也会改变(位置没变),与设置frame属性的效果相同。bounds属性到底是什么,该如何使用呢?这里首先将frame与bounds属性各自的作用归纳如下。

  • frame属性是以父元素(superview)的本地坐标系为基准的位置及尺寸。
  • bounds属性是以自身的本地坐标系为基准的位置及尺寸。

图2-18是上述归纳的示意图,父元素追加到UIWindow中,子元素再追加到父元素中。

图2-18 frame与bounds的关系

此时,父元素的frame.origin属于UIWindow的本地坐标系,值为(100,15)。如此对应,子元素的frame.origin属于父元素的坐标系,值为(0,80)。另外,bounds.origin都是以自身坐标系为基准的,因此值都为(0,0)。

frame属性用于设置UIView位置及尺寸,而bounds属性用于在自己的区域内绘制其他子元素,或者用于判断用户触摸了自身坐标系的什么位置等。

我们可以看一个具体的例子(见图2-19)。例如在坐标(50,15)处已有UIViewA。现在在UIViewA中追加与其相同大小的标签子元素label。

图2-19 追加与UIViewA相同大小的标签

此处的问题是,标签的frame属性中正确的设置值为哪一个。

  • label.frame = UIViewA.frame。
  • label.frame = UIViewA.bounds。

初看起来正确的答案好像是1,但其实正确的答案为2。frame属性属于父元素的本地坐标系范畴。label.frame中设置的值,从UIViewA的本地坐标系来考虑,应该是(0,0,100,80)。但是 UIViewA.frame的值为(50,15,100,80)。因此向label.frame中设置的正确值是,同样以UIViewA本地坐标系为基准的 UIViewA.bounds,即(0,0,100,80)。