如何使用故事板、initWithCoder、viewDidLoad 和 viewDidLayoutSubviews 有效地管理屏幕尺寸更改和重绘?

Posted

技术标签:

【中文标题】如何使用故事板、initWithCoder、viewDidLoad 和 viewDidLayoutSubviews 有效地管理屏幕尺寸更改和重绘?【英文标题】:How do I efficiently manage screen size changes and redrawing with storyboards, initWithCoder, viewDidLoad and viewDidLayoutSubviews? 【发布时间】:2018-07-14 04:41:33 【问题描述】:

我正在使用情节提要布局来设置视图布局。

我同时支持 iPhone 和 iPad 布局。

当使用initWithCoder: 创建视图时,它最初是使用我上次在 Interface Builder 中查看的设备的帧大小创建的。

如果我在界面构建器中使用 iPhone X 布局进行设计,然后在 iPad 上构建和运行,则视图最初是使用 iPhone X 屏幕尺寸创建的。然后调用 viewDidLayoutSubviews: 并将屏幕尺寸更新为正确的 iPad 尺寸。

    子视图在 UIViews 中使用drawRect 来绘制视图图形。我这样做是为了可以通过代码更改图形颜色。我更改了颜色变量,然后在视图上调用setNeedsDispay 以使用CGGraphicsContext 命令用新颜色重绘视图。

    它还允许我以任何大小绘制任何图形图像。并且有很多图形,这意味着我不必在我的捆绑包中包含所有 1x、2x 和 3x 尺寸的不同图像。都是动态绘制的。

    其中一些图像是在视图加载时布局的,而不是在 Interface Builder 中。所以我检查屏幕大小并相应地绘制按钮大小和位置。

发生的情况是,viewDidLoad 被调用并根据初始屏幕尺寸绘制图形。

然后调用viewDidLayoutSubviews,我必须更新我放置的子视图的绘图,根据新的屏幕尺寸手动重新定位它们,然后在它们上调用drawRect。我觉得这对设备来说只是不必要的额外工作。

除此之外,viewDidLayoutSubviews 会因为其他原因而被调用,然后只是在 viewController 的初始加载时调整视图的大小。所以每次调用它都会重新绘制子视图,即使它们不需要它。

而且,如果我正在运行的设备与我在 Interface Builder 中使用的设备相同,它不会调用 viewDidLayoutSubviews。我不能让视图布局那里的子视图,因为不能保证它会被调用。

到目前为止,我的解决方案是创建一个变量来跟踪屏幕宽度。我在viewDidLoad 中设置了变量。如果它以 iPhone X 大小创建视图,我的 screenWidthTracker = 1125。当viewDidLayoutSubviews 被调用时,我将当前屏幕尺寸与screenWidthTracker 进行比较。

if (self.view.frame != screenWidthTracker) 
     [[NSNotificationCenter defaultCenter] postNotificationName:@"updateView" object:self];
;

如果视图改变了大小,它会发送一条消息来重绘视图。我作为子视图手动放置的任何视图都注册为侦听@“updateView”。

有没有更好的方法来管理这个?有没有一种方法只在屏幕尺寸发生变化而不是在更新其他子视图的位置时才被调用?我应该使用 viewDidAppear 吗?我觉得这似乎太晚了,我不希望用户看到按钮大小的更新。

【问题讨论】:

你不是真的在打电话给drawRect,对吗?那是绝对违法的。您真的必须手动完成所有这些工作吗?这不是自动布局的用途吗?感觉就像您对子视图布局和绘图在 ios 上的工作方式有很大的误解,因此做的工作比您必须做的要多得多。理想情况下,除了 drawRect 实现(实现它,而不是调用它)之外,您不需要 任何 代码。 我想我可能对子视图布局和绘图的工作方式有很大的误解。至于调用 drawRect 我一定是不清楚的。我只是说 drawRect 在 vi​​ewDidLayoutSubviews 之后被调用。如果我需要更新它,我会调用 setNeedsDisplay,我认为它会调用 drawRect - 除非您认为这是我的误解所在。 这只是两种观点的问题。在他们两个中,我都有未确定数量的图标分成两行并调整它们的大小,以便它们适合并均匀排列。所以取决于我是否有 6 或 12 个大小和位置变化,所以我发现这比其他方式更容易处理。我想我可以放入一个 stackView 并将它们添加到其中。 【参考方案1】:

一般来说,根据您给出的描述,我会说您最多应该通过使用自动布局(您可以完全在情节提要中配置)然后实施尽可能多的工作仅限 drawRectviewDidLayoutSubviews — 后者仅在您需要时。

一个非常常见的策略是在 first 调用viewDidLayoutSubviews 时使用布尔标志设置初始条件,之后不再使用它。通过实现willTransition(to:with:) 可以检测到 启动后(而不是 启动时)屏幕尺寸的变化——即使这样你也应该检查旧尺寸和新尺寸不一样(180 度旋转),如果它们不一样,则什么也不做。

【讨论】:

我不关心旋转,因为我不支持它。我喜欢为此设置一个布尔值的想法。我想我只是偏执狂,我没有想到或不知道某些情况会调用 viewDidLayoutSubviews 并且不会在第一次完成完全调整大小。因此,作为一个额外的安全措施,我开始跟踪视图的实际大小,该视图包含请求数量不确定的元素的子视图。我 99% 的视图使用自动布局。但是在自动布局方面更多地考虑它让我考虑将它们添加到 stackView 中。谢谢。

以上是关于如何使用故事板、initWithCoder、viewDidLoad 和 viewDidLayoutSubviews 有效地管理屏幕尺寸更改和重绘?的主要内容,如果未能解决你的问题,请参考以下文章

试图关闭故事板呈现的弹出框

iOS5 Storyboard UIViewController 的故事板调用了哪个init方法?

故事板中的自定义 UIView

UItabbar 项目未显示情节提要参考

故事板:如何使用故事板为 UIPageViewController 设置初始视图控制器?

如何将项目从使用故事板转换为不再使用故事板?