uiviewcontroller生命周期行为

TL;DR: There is a nice way to reuse the code of view controller’s life cycle’s methods. It comes in handy when an OOP hierarchy can’t seem to help accomplish such refactoring. It is called View Controller Life Cycle Behavior. Fill free to check out an example right away.

TL; DR:有一种很好的方法可以重用视图控制器生命周期方法的代码。 当OOP层次结构似乎无法帮助完成这种重构时,它会派上用场。 这称为视图控制器生命周期行为 。 免费填写以立即查看示例

介绍 (Introduction)

Once in a while we find out that some of our view controllers’ declarations look messy and oversized due to recurring configuration methods which happen to have been called from nowhere but a life cycle’s method. At the beginning of a project it seems like no big deal, yet it still ends up being something in need of an emergent refactoring. And there are cases where we cannot use a superclass extraction (which is the first thing coming to mind), because not every view controller provides the same combination of functionality or, in other words, behaves like that. I would like to introduce View Controller Life Cycle Behavior.

偶尔,我们发现一些视图控制器的声明看起来很杂乱且过大,这是由于重复使用的配置方法恰巧是从生命周期的方法调用而来的。 在项目开始时,似乎没什么大不了的,但是最终仍然需要紧急重构。 在某些情况下,我们不能使用超类提取(这是我要想到的第一件事),因为并非每个视图控制器都提供相同的功能组合,或者换句话说, 其行为类似。 我想介绍一下View Controller生命周期行为

抽象行为 (Abstract behavior)

A view controller might be doing different kinds of things simultaneously during its life cycle’s methods’ execution meaning there may be an array of behaviors, which is to be stored in some property. Hence we will have to provide a protocol — an abstract behavior:

视图控制器在其生命周期的方法执行期间可能同时执行多种操作,这意味着可能存在一系列行为,这些行为将存储在某些属性中。 因此,我们将必须提供一个协议-一种抽象行为:

  1. Our goal is to try and reuse life cycle’s functionality, so it makes sense to include counterparts of viewDidLoad(), viewWillAppear(_:), etc., in the protocol. Besides, we’re going to have to know about the current “behavioral” view controller (e.g., to obtain access to its properties), so we pass it in each time. Every method is given a default empty implementation as they should not be required, because we don’t override every available method in our custom view controllers.

    我们的目标是尝试并重用生命周期的功能,因此在协议中包括viewDidLoad()viewWillAppear(_ :)等对应项是有意义的。 此外,我们将不得不了解当前的“行为”视图控制器(例如,获取对其属性的访问权限),因此我们每次都会传递它。 每个方法都有默认的空实现,因为不需要,因为我们不会在自定义视图控制器中覆盖所有可用方法。

  2. Now we need to find a way to embed our methods’ calls into original ones of a view controller. Of course, we could manage to do it using a good old superclass, but, at this rate, we would not be able to call super.viewDidLoad() until after we have added a behavior in self.viewDidLoad() (otherwise applying a behavior which executes something once the view have been loaded would be impossible, because we can’t really pass in anything custom into the initializers of a view controller displaying content), exposing the array of behaviors to every derived class at that. A private child view controller to the rescue! We just provide a public method in view controller’s extension. It receives needed behaviors, then creates an instance of LifeCycleBehaviorViewController (which wraps all passed in behaviors and executes their methods at each life cycle’s stage respectively) and adds it as a child. Thus, its life cycle synchronizes with the parent’s one.

    现在,我们需要找到一种方法来将方法的调用嵌入到视图控制器的原始方法中。 当然,我们可以使用一个很好的旧超类来做到这一点,但是以这种速率,直到在self.viewDidLoad()中添加了一个行为之后,我们才能够调用super.viewDidLoad () (否则应用一旦加载了视图,就无法执行将执行某些操作的行为,因为我们无法真正将任何自定义传递给显示内容的视图控制器的初始化程序,从而将行为数组暴露给每个派生类。 私人儿童视野控制器可以解救! 我们只是在视图控制器的扩展中提供了一个公共方法。 它接收所需的行为,然后创建LifeCycleBehaviorViewController的实例(该实例包装所有传入的行为并分别在每个生命周期的阶段执行其方法),并将其作为子对象添加。 因此,其生命周期与父母的生命周期同步。

The child view controller is just a dummy, which is why its view must be hidden. It will not affect life cycle’s methods’ calling.

子视图控制器只是一个虚拟对象,因此必须隐藏其视图。 它不会影响生命周期方法的调用。

(Example)

I will demonstrate 2 close-to-real behavior examples in a pretty simple abstract app. The behaviors are able to be implemented using either concrete objects or protocols. You can find examples of the former here, and of the latter here.

我将在一个非常简单的抽象应用程序中演示2个接近真实的行为示例。 可以使用具体对象或协议来实现行为。 你可以发现前者的例子在这里 ,而后者的位置

Let’s assume you have an app where view controllers are embedded in a navigation controller, and you want them to have navigation bars with some primary color (implying some of them use the default color meaning you cannot utilize a superclass, as the coloring of the bar may be randomly combined with other behaviors). Moreover, you, say, decide to support iOS 10, so you’ll have to deal with translucent bar’s issues (sometimes, there will be a juncture when some table views will have a peculiar top inset on iOS versions where there’s no such thing as Safe Area. Those are properties influencing this behavior. One of the easiest solutions here is setting edgesForExtendedLayout to an empty array). So, off and on, you’ll have to rectify those situations in view controllers.

假设您有一个应用程序,其中视图控制器嵌入在导航控制器中,并且您希望它们的导航栏具有一些原色(这意味着其中一些使用默认颜色,这意味着您不能使用超类,因为该栏的颜色可能会与其他行为随机组合)。 而且,例如,您决定支持iOS 10,因此您必须处理半透明条的问题(有时,当某些表视图在iOS版本上具有特殊的顶部插图时,会有一个关头,其中没有这样的事情:安全区域:这些是影响此行为的属性 ,此处最简单的解决方案之一是将edgeForExtendedLayout设置为一个空数组。 因此,无论如何,您都必须在视图控制器中纠正这些情况。

具体对象的实现 (Concrete object implementation)

Let’s look at aforementioned behaviors’ implementations:

让我们看一下上述行为的实现:

Image for post
This behavior colors a navigation bar along with all its subviews keeping nice contrast
此行为为导航栏及其所有子视图着色,从而保持良好的对比度
Image for post
This behavior helps to get rid of the weird space above some table views on older iOS versions
此行为有助于摆脱旧版本iOS上某些表视图上方的怪异空间

Then, all we have left to do is add the behaviors to a view controller:

然后,我们要做的就是将行为添加到视图控制器中:

Image for post

The status bar should be light, because I’ve chosen blue as the primary color of the app. It also can be refactored a little further, as it’s shown at the bonus section.

状态栏应该是浅色的,因为我选择了蓝色作为应用程序的主要颜色。 如奖金部分所示,它也可以进一步重构。

Image for post

协议实施 (Protocol implementation)

There’s a way to somewhat refactor the previous implementation: we can use a generic view controller superclass which will add passed in behaviours on its own, relieving subclasses of that. The problem is, there’s no “arrays of generic types”, so we are not able to do this:

有一种方法可以在某种程度上重构先前的实现:我们可以使用通用的视图控制器超类,该超类将自己添加传入的行为,从而减轻了该类的子类。 问题是,没有“泛型类型的数组”,因此我们无法做到这一点:

class SomeVC: SuperVC<[SomeBehavior, AnotherBehavior, ...]> { ... }

The solution is creating objects comprising several behaviors at once. It’s easily done using them as protocols! Let’s modify existing behaviors a bit:

解决方案是同时创建包含多个行为的对象。 使用它们作为协议很容易做到! 让我们修改一下现有行为:

Image for post

If you find yourself needing a stored property inside of the behavior, just add to the protocol’s declaration and provide it with a value within a concrete implementation.

如果发现自己在行为内部需要存储的属性,则只需添加到协议的声明中,并在具体实现中为其提供值即可。

Image for post

The combination:

组合:

Image for post

It’s as simple as this: we just transform behaviors into protocols, then create implementations, make them conform to behaviors, producing all combinations we want.

就这么简单:我们将行为转换为协议,然后创建实现,使其符合行为,生成我们想要的所有组合。

Now it’s time to provide a base view controller class which is going to manage all these behaviors:

现在是时候提供一个基本视图控制器类来管理所有这些行为了:

import UIKit


class BehaviorContainingVC<Behavior: ViewControllerLifeCycleBehavior>: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addBehaviors([Behavior()])
    }
    
}

To be able to do the instantiation, don’t forget to declare init() in the ViewControllerLifeCycleBehavior protocol.

为了能够进行实例化,请不要忘记在ViewControllerLifeCycleBehavior协议中声明init()

That is how our view controllers look like now:

这就是我们的视图控制器现在的样子:

final class FirstViewController:
BehaviorContainingVC<PrimaryNavBar>
{ ... }final class SecondViewController:
BehaviorContainingVC<PrimaryNavBar_EdgesForExtendedLayout>
{ ... }

And that’s it, you don’t have to do anything else in order to reuse this functionality. There are 2 issues/caveats:

就是这样,您无需执行任何其他操作即可重用此功能。 有2个问题/警告:

  1. The naming of combinations is awful and there’s no clean way to resolve this. You might use nested generic objects receiving 1 behavior, 2 behaviors, etc., but it seems to be only worse.

    组合的命名很糟糕,没有解决之道的干净方法。 您可以使用嵌套的通用对象接收1个行为,2个行为等,但这似乎只会更糟。
  2. You’ll have to resolve protocols’ default implementations’ conflicts. Imagine, you set edges for extended layout in viewWillAppear(_:)’s counterpart (where all navigation bar’s work happens as well). You’ll get this quite vague error:

    您必须解决协议的默认实现的冲突。 想象一下,您在viewWillAppear(_ :)的对应对象中设置了扩展布局的边缘(所有导航栏的工作也都在其中进行)。 您将得到这个相当模糊的错误:

Image for post

As a matter of fact, it tells you that the compiler can’t decide whose default implementation to choose — both protocols provide one, not having unambiguous type constraints in their extensions. The only way out is to resolve it manually :(. You can extract the functionality, though. It’ll lead to sufficiently concise solution. Namely:

实际上,它告诉您编译器无法决定选择哪个默认实现—两种协议都提供了一个,并且在其扩展中没有明确的类型约束。 唯一的解决方法是手动解决:(。您可以提取功能,但这将导致足够简洁的解决方案。即:

Image for post
We extract conflicting functionality into a separate method
我们将冲突的功能提取到单独的方法中
Image for post

Then, we resolve the conflict with help of the new methods:

然后,我们借助新方法解决冲突:

Image for post

You can check this solution here.

您可以在此处检查此解决方案。

Despite those issues, I still believe, it’s a nice way to refactor your view controllers’ life cycle’s functionality, because, regardless of what’s going on under the hood, the controllers look clean due to the encapsulation of quite a big portion of UI and/or business logic.

尽管存在这些问题,我仍然相信,这是重构视图控制器生命周期功能的一种好方法,因为无论幕后发生了什么,由于封装了很大一部分的UI和/,控制器看起来很干净或业务逻辑。

奖金 (Bonus)

There is a way to extract status bar logic into the view controller superclass:

有一种方法可以将状态栏逻辑提取到视图控制器超类中:

Image for post

If you don’t like such coupling between base superclass and a specific behavior, feel free to go further, add a flag to the base behavior protocol (something like “affectsStatusBarStyle”) and use it in the condition here.

如果您不喜欢基本超类与特定行为之间的这种耦合,请继续进行下去,在基本行为协议中添加一个标志(类似“ affectsStatusBarStyle”),然后在此处的条件中使用它。

结论 (Conclusion)

This approach really helps when you don’t want to “paint” your controllers in storyboards, or when some of your view controllers trigger analytics’ events and so on. You have more than one option to implement it, so it’s a pretty flexible solution worth considering!

当您不想在情节提要中“绘制”控制器时,或者当某些视图控制器触发分析事件时,这种方法确实有用。 您有多种选择来实现它,因此它是一个非常灵活的解决方案,值得考虑!

Thanks for reading!

谢谢阅读!

Follow me on Twitter, LinkedIn, GitHub or Reddit! Contact me, if you have any questions or comments :)

TwitterLinkedInGitHubReddit上关注我! 如果您有任何疑问或意见,请与我联系:)

翻译自: https://medium.com/swlh/uiviewcontroller-lifecycle-behavior-8c023f433a87

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值