从主视图控制器中提取@IBOutlets 属性值

Extracting @IBOutlets properties values out of the main view controller

从一开始我就一直在使用 MVP 模型来让事情继续下去,但现在,几个月后,我意识到我的大部分项目都有 "Massive View Controllers"。扩展一开始很好地整理了实际的视图控制器文件,但从技术上讲,代码仍在视图控制器中。

在我的上一个项目中,一个更实用的方法解决了我的问题,我进行了应用内购买,之后用户解锁了一些新功能。我们假设整个 StoreKit 逻辑都在执行它的工作,购买已成功完成,您即将向用户提供新功能。

为了处理这个问题,我在主视图控制器 (PreferencesViewController) 中有一个函数 func unlockFeatures,当应用内购买成功时,它由观察者触发。

class PreferencesViewController: UIViewController {

    ...
    @obc func unlockFeatures {
        InAppPuchaseButton.isEnabled = false
        InAppPuchaseButton.applyOpacity(opacity: 1.0)
    }
    ...
}

此时,当我只需要启用和更改一个按钮的不透明度时,一切看起来都很干净。我的问题是如何处理 func unlockFeatures 中需要处理多行 UI 代码的情况,如下所示:

class PreferencesViewController: UIViewController {

    ...
    @obc func unlockFeatures {
        InAppPuchaseButton.isEnabled = false
        InAppPuchaseButton.applyOpacity(opacity: 1.0)
        InAppPuchaseButton.isHidden = false
        RefreshLabel.text = "DOWNLOADING EVENTS"
        self.activityIndicator.stopAnimating()
        //And keep going for 50 lines more of code!!
    }
    ...
}

对我来说,它看起来很混乱,我想将此函数提取到 PreferencesViewController 之外,并在需要时调用它。有没有办法在视图控制器 class 之外提取所有这些 UI 逻辑,或者这是一种不好的做法?

应用内购买示例非常好。我们想在这里做的是提供一个单一的集中对象,可以(例如):

  • 作为不涉及接口的IAP相关代码的轨迹,比如作为SKPaymentTransactionObserver

  • 充当应用程序其余部分的网关,以便能够询问用户是否进行了购买(当然我们的对象会通过查询用户默认值知道答案,但那将是public API)

  • 隐藏的实现细节

将这些功能从视图控制器中分离出来是有意义的,因为它们与控制任何视图无关

所以这是使用 助手 class 的绝好机会。一个典型的实现是一个 class 分配一个单例实例,我们获取并存储在应用程序委托或根视图控制器的 属性 中,其中任何一个都是持久的并且可以从任何地方轻松访问在应用程序中。


在更广泛的背景下,我一直使用这种辅助对象来解决您描述的那种问题。例如,在一个游戏应用程序中,我在主视图控制器中有 很多 代码,直到我将几个操作区域分解为辅助 classes,因为它们确实有与视图的控制无关。例如,计时器的逻辑和分数的维护进入了一个辅助对象。当我们进入游戏的下一阶段时,决定下一步做什么的 "state machine" 变成了一个辅助对象。等等。


在您修改后的问题中,您说:

To me it looks cluttered and I would like to extract this function outside the PreferencesViewController and call it just when I need it. Is there a way to extract all this UI logic outside the view controller class or this is a bad practice?

这里什么都没有"cluttered";一切都在一个功能中,这是正确的。好的做法是让 UI 操作 视图控制器 class 中,但可能 从其他地方调用 它(例如由我在回答中描述的助手提供)。实际上 I 会做的是让助手 post 收到通知,这样它就不知道会发生什么;它只是喊 "Okay, the user purchased!" 并且视图控制器收到通知并调用该方法。