iOS - 在哪里初始化视图

Posted

技术标签:

【中文标题】iOS - 在哪里初始化视图【英文标题】:iOS - Where to initialize views 【发布时间】:2013-09-06 14:53:31 【问题描述】:

如果我想以编程方式初始化视图,这应该发生在视图控制器生命周期的哪个位置?

最初的直觉是 loadView。但是,在这里,我们还没有视图本身的框架(计算视图的大小/位置所必需的)。 viewDidLoad 也是如此。

下一个直觉是viewWillAppear——在这里我们(最终)保证了视图的框架。但是,这有可能在整个 vc 生命周期中被多次调用。 viewDidAppear 等...

最后,我找到了 viewWillLayoutSubviews。这适用于大多数静态布局的初始化——但是,每当任何视图移动时,都会再次调用它(与 viewWillAppear 的问题相同)。

我已经看到建议在 loadView 中初始化视图并在 viewWillLayoutSubviews 中设置它们的框架(因为设置框架应该是幂等的,谁在乎它是否被称为一对次)。但是为什么苹果那么强烈地鼓励initWithFrame:作为UIViews的标准初始化方法(https://developer.apple.com/library/ios/documentation/windowsviews/conceptual/viewpg_iphoneos/CreatingViews/CreatingViews.html)呢?

将我所有的 UIViewController 子类化为具有 initWithViewFrame: 方法会不会很疯狂?这样我可以传入一个框架,立即在 loadView 中手动设置它并完成它?还是在 viewWillAppear 中有一个 viewHasBeenFormatted 标志,如果没有设置,调用视图的格式然后设置它会更好吗?

或者这只是苹果的说法“使用界面生成器,否则你就完蛋了”?

感谢任何帮助!

编辑- 在我的意思是 viewWillAppear 的地方不小心写了 loadView(在最后一段)

更新-我想我已经接受了这样一个事实,即没有地方可以

    框架是众所周知的 代码只会运行一次(设置时)

看起来您应该 initWithFrame: 您在 viewDidLoad 中的所有视图(但我猜该视图的内容不应将该框架视为远程最终的?因为它是根据假设得出的?呃......)。然后在 layoutSubviews 中重新设置它们的框架。并确保手动处理初始布局和布局之间的差异,因为那里移动了视图......伙计,我觉得我必须遗漏一些东西......(哈哈否认......)

我猜是这样,或者提交并使用 IB。

update2- viewWillLayoutSubviews 将在其子视图之一被调整大小时被调用。所以它仍然不合格,因为它不符合我正在寻找的所需特征的属性 2。 :(

【问题讨论】:

【参考方案1】:

如果您正在使用 IB 进行布局,则可以在 viewDidLoad 中进行额外的视图初始化(例如,如果您需要做 IB 无法很好处理的事情,或者如果您有 UIView 子类IB 不支持的属性)。或者,如果您不使用 IB,documentation 表示您应该使用 loadView 手动初始化您的视图层次结构。

不过,您是对的,此时您不能依赖帧的准确性。因此,您可以通过每个视图的 autoResizingMask 属性、布局约束(如果您是 iOS 6 及更高版本)和/或覆盖 layoutSubviews 来完成布局。

我通常的做法是在 IB 中进行某种程度的布局,然后在 viewDidLoad 中做我需要做的任何其他事情(非平凡的布局、自定义类等)。然后,如果我有布局来确定 autoResizingMask 不涵盖(我支持到 iOS 5),我会覆盖 viewWillAppear(或 layoutSubviews,如果我是子类化 UIView)并做一些像素数学。我在UIView 上有一个类别可以帮助解决这个问题,其中包括:

-(void)centerSubviewHorizontally:(UIView *)view pixelsFromTop:(float)pixels;
-(void)centerSubviewHorizontally:(UIView *)view pixelsBelow:(float)pixels siblingView:(UIView *)sibling;

【讨论】:

是的,我没有使用 IB。我可以在 loadView 中初始化层次结构(谁是谁的子视图)。问题来自大多数需要(/鼓励)“initWithFrame”的初始化,在我知道父视图的大小/形状之前我不应该考虑它。 autoResizingMasks 不足以满足我需要的布局(不使用 IB 的原因之一)。所以基本上,我听到的是没有一个地方可以保证在 vc 的生命周期中只被调用一次,它也可以访问视图的最后一帧。嘘。让我伤心。 :( 对于未来的读者还有一点需要注意:layoutSubviews 是 UIView 上的方法,而不是 UIViewController。因此,要覆盖它就是使用自定义视图。 (或者使用 viewWillLayoutSubviews,它位于 viewController 上) 我不会担心多次进行布局。我的方法是尽可能在 .xib 中进行初始化,并在 viewDidLoad 中涵盖所有其他内容。有关详细信息,请参阅我的更新。【参考方案2】:

视图控制器应该initWithFrame: 方法。我在所有代码中所做的(我从不使用 IB)是让默认的 loadView 做自己的事情。我在viewDidLoad 中创建和设置所有子视图。此时,视图控制器的框架至少有一个合理的值。所有子视图都可以根据视图控制器视图的初始大小使用自己的健全框架创建。使用正确的 autoresizingMask 值,这可能就是您所需要的。

如果您需要更具体的子视图布局,请将相应的布局代码放在viewWillLayoutSubviews 方法中。这将处理任何视图控制器视图框架更改,包括旋转、通话状态栏等。

【讨论】:

你还没有切换到自动布局? 自动布局不是IB的功能吗?那就不要。我从来没有使用过它。我用代码做所有事情。 IB对我来说毫无意义。我已经编写 iOS 应用程序超过 5 年了,我从来没有觉得需要 IB。 您可以在没有 IB 的情况下使用自动布局。节省大量计算。 你的意思是约束吗? 是的。据我所知,自动布局是指根据约束计算帧的机制。 IB 只是它的一个 UI。【参考方案3】:

如果您不使用界面构建器,您应该覆盖 loadView 并在那里初始化视图。如果你使用自动布局,你也可以在那里添加你的约束。如果您不使用自动布局,您可以覆盖视图的 layoutSubviews 方法来调整框架。

【讨论】:

根据文档,添加约束的正确位置是 updateViewConstraints 我认为 updateViewConstraints 应该用于更改约束,而不是用于初始化。如果您使用界面生成器,则会在 loadView 期间添加约束。 参考文档状态“您可以在子类中重写此方法,以便为视图或其子视图添加约束”

以上是关于iOS - 在哪里初始化视图的主要内容,如果未能解决你的问题,请参考以下文章

字符串的静态 NSArray - 如何/在哪里初始化视图控制器

对于从 IB 加载的 UItableViewController,非视图相关的初始化代码放在哪里

如果不在 viewDidLoad 中,我应该在哪里进行初始网络调用(例如,最初填充提要)?

我应该在 iOS/OS X 应用程序中在哪里初始化我的单例?

iOS View Based Application 模板:“application:didFinishLaunchingWithOptions:” viewController 在哪里初始化?

在 Flux/React.js 中,在哪里初始化 jQuery 插件?