2012-09-21 39 views
55

我正在开发使用iOS 6自动布局iOS AutoLayout - 获取帧大小宽度

我想记录显示视图帧宽的消息。

我可以在屏幕上看到textView。

但我得到的宽度和高度为零,我错过了什么?

NSLog(@"textView = %p", self.textView); 
NSLog(@"height  = %f", self.textView.frame.size.height); 
NSLog(@"width  = %f", self.textView.frame.size.width); 

textView = 0x882de00 
height  = 0.000000 
width  = 0.000000 

回答

56

我认为自动布局在您调用此功能时没有时间布置您的视图。在调用viewDidLoad时,自动布局并未发生,因为它在视图加载之后立即被调用,并且只有在视图放入视图控制器的视图层次结构中并最终布局之后才会调用它(在视图的layoutSubviews方法中)。

编辑:此答案指出为什么问题中的场景不起作用。 @dreamzor's answer指出为了解决它,放置代码的位置。

+0

我试图在'viewDidLoad'中记录消息。自动布局完成后调用哪个方法。我问的原因是因为我想在用户可以与textView交互之前计算宽度。所以记录对象描述可能无助于bcuz我想使用宽度进行其他计算。 – user1046037

+1

不,'viewDidLoad'发生在自动布局之前。在视图添加到另一个视图(这是在调用viewWillAppear后不久,但在viewDidAppear之前可能不可靠)之后,自动布局会懒惰地发生。知道唯一可靠的方法是在视图控制器的视图上知道layoutSubviews何时完成。 – Jesper

+1

你为什么试图让框架呢?如果你想根据它进行计算并做自己的布局,你最终可能会与自动布局战斗。 – Jesper

28

解决方案

  • 将完成自动布局之前viewDidAppear

原因

  • viewDidLoad代码发生。所以位置尚未由xib中指定的自动布局设置
  • viewDidAppear在自动布局完成后发生。
+1

我把代码放在viewDidAppear中,但仍然是,我的视图的NSLog框架显示了零。 – dreamzor

+0

对于iOS 6.0,其实viewDidAppear在viewDidLayoutSubviews之前被调用,所以dreamzor是正确的。 – vedrano

+4

在iOS 6中,基于我的测试,我在第二个控制器中使用了导航控制器,viewDidLayoutSubviews在viewDidAppear之前被调用,但在viewDidLayoutSubviews中,子视图的帧宽仍然为零。仅在'viewDidAppear'中,子视图的框架宽度似乎具有正确的值。 – user1046037

147

其实以上答案并不完全正确。我跟着他们,一次又一次地得到了零。

诀窍是把你的帧有关的代码到viewDidLayoutSubviews方法,该方法

通知其视图只是布局它的子视图的视图控制器。

不要忘记这个方法被多次调用,而不是ViewController生命周期的一部分,所以在使用这个时要小心。

希望这对别人有帮助。

只是想补充说,对于我的程序没有横向模式不使用自动布局是简单得多...我想,虽然= d

+9

从经验来看,这是自动布局完成工作后的最早回调。例如,我推荐使用BOOL来第一次调用这个函数,并执行与帧相关的逻辑。这不是非常优雅,但完成工作,直到有适当的回调特定的自动布局限制。 – ArkReversed

+3

@dreamzor,我怎么知道它是否是最后一个viewDidLayoutSubviews?我发现在iOS 7上,只有一个viewDidLayoutSubviews将在viewDidAppear之前被调用,但在iOS 8上,我在viewDidAppear之前看到多个viewDidLayoutSubviews。这很烦人,因为第一个viewDidLayoutSubviews会在iOS 8上给出错误的帧大小,所以我必须第一次忽略它。 – Wingzero

+0

@Wingzero在我的项目中,第五次是正确的,那么我怎么能忽略其他呢? –

13

其实我设法布局更新的viewDidLoad内我的代码之前:

override func viewDidLoad() { 
     super.viewDidLoad() 

     println("bounds before \(self.previewContainer.bounds)"); 
     //on iPhone 6 plus -> prints bounds before (0.0,0.0,320.0,320.0) 

     self.view.setNeedsLayout() 
     self.view.layoutIfNeeded() 

     println("bounds after \(self.previewContainer.bounds)"); 
     //on iPhone 6 plus -> prints bounds after (0.0,0.0,414.0,414.0) 

     //Size dependent code works here 
     self.create() 
    } 

UPDATE:这似乎并没有工作了

25

包含在您的viewDidLoad()中

self.view.setNeedsLayout() 
self.view.layoutIfNeeded() 

在访问您的v iew.frame.size.width

2

的iOS自动版式 - 获得帧大小宽度

-(void) layoutSubviews{ 
     [self layoutIfNeeded]; 
     //right you code to set frame its will work to get frame and set frame. 
     CALayer *bottomBorder = [CALayer layer]; 
     bottomBorder.frame = CGRectMake(0.0f, bkView.frame.size.height - 1, bkView.frame.size.width, 1.0f); 
     bottomBorder.backgroundColor = [UIColor blackColor].CGColor; 
     [bkView.layer addSublayer:bottomBorder]; 
    } 
5

以上都不是答案的全部工作对我来说,除了viewDidLoad但是这并没有显示的副作用我想要什么,直到观看动画后,看起来很差。

viewDidLayoutSubviews应该是运行依赖于自动布局是完整的代码正确的地方,但正如其他人指出,这是在最近的IOS版本称为多次,你可以不知道这是最后的通话。

所以我解决了这个小黑客。在我的故事板中,mySubview应该小于其包含self.view。但是当第一次调用viewDidLayoutSubviews时,mySubview的宽度仍然为600,而self.view似乎设置正确(这是iPhone项目)。所以我所要做的就是监控后续调用并检查相对宽度。一旦mySubview小于self.view我可以确定它已正确布置。

override func viewDidLayoutSubviews() { 
    super.viewDidLayoutSubviews() 

    if self.mySubview.bounds.size.width < self.view.bounds.size.width { 

     // mySubview's width became less than the view's width, so it is 
     // safe to assume it is now laid out correctly... 

    } 
} 

这具有不依赖于硬编码数字的优势,因此它可以在所有iPhone外形因素上工作,例如。当然,在所有情况下或所有设备上,它可能都不是万能的,但可能有许多巧妙的方法来做相似尺寸的类似检查。

不,我们不应该这样做,但直到苹果给我们一些更可靠的回调,我们都坚持下去。

7

这真是奇特的功能。但我发现:

如果你想获得帧,而无需使用layoutsubviews方法,使用这一个:

dispatch_async(dispatch_get_main_queue(), ^{ 
     NSLog(@"View frame: %@", NSStringFromCGRect(view.frame)); 
    }); 

这真是奇怪!

+0

非常感谢你sam – Subramani

0

您只能在第一次布局过后查看大小。或者只需调用下面的方法,然后在获得实际宽度后查看。

[yourView layoutIfNeeded];