我在iOS应用程序中有VC,这些应用程序具有很多UI控件。现在,当处于特定状态时,我将需要替换或“模拟”其中的某些控件。在某些情况下,这只是禁用按钮动作,但在某些情况下,需要用完全不同的东西替换发生的动作。
我真的不喜欢在代码库中乱七八糟的检查。
if condition {
...Special/disabled functionality
} else {
...Normal functionality
}
在Android中,我可以将每个Fragment / Activity子类化,并在那里构建功能,然后在插入Fragment或启动活动时执行if / else。
但是在带有Storyboards / IBActions和Segues的iOS上,UI和VC确实紧密地结合在一起。您要么复制UI视图,要么向已经很大的VC添加很多挑剔的代码。
在iOS中处理此问题的最佳方法是什么?
我想避免的示例代码:
//Before:
class SomeViewController : UIViewController {
@IBAction onSomeButton() {
checkSomeState()
doANetworkRequest(() -> {
someCompletionHandler()
updatesTheUI()
}
updateTheUIWhileLoading()
}
@IBAction onSomeOtherButton() {
checkAnotherState()
updateUI()
}
}
//After:
class SomeViewController : UIViewController {
@IBAction onSomeButton() {
if specialState {
doSomethingSimpler()
} else {
checkSomeState()
doANetworkRequest(() -> {
someCompletionHandler()
updatesTheUI()
}
updateTheUIWhileLoading()
}
}
@IBAction onSomeOtherButton() {
if specialState {
return // Do nothing
} else {
checkAnotherState()
updateUI()
}
}
}
答案 0 :(得分:1)
我建议使用MVVM (Model - View - ViewModel) pattern。您将ViewModel
传递给控制器,并将所有操作委托给它。您还可以使用它来设置视图样式,并确定其中的某些视图是隐藏还是禁用等。
让我们想象一个购物应用程序,您的专业用户可以在其中获得10%的折扣并可以使用免费送货选项。
protocol PaymentScreenViewModelProtocol {
var regularPriceString: String { get }
var discountedPriceString: String? { get }
var isFreeShippingAvailable: Bool { get }
func userSelectedFreeShipping()
func buy()
}
class StandardUserPaymentScreenViewModel: PaymentScreenViewModelProtocol {
let regularPriceString: String = "20"
let discountedPriceString: String? = nil
let isFreeShippingAvailable: Bool = false
func userSelectedFreeShipping() {
// standard users cannot use free shipping!
}
func buy() {
// process buying
}
}
class ProUserPaymentScreenViewModel: PaymentScreenViewModelProtocol {
let regularPriceString: String = "20"
let discountedPriceString: String? = "18"
let isFreeShippingAvailable: Bool = true
func userSelectedFreeShipping() {
// process selection of free shipping
}
func buy() {
// process buying
}
}
class PaymentViewController: UIViewController {
@IBOutlet weak var priceLabel: UILabel!
@IBOutlet weak var discountedPriceLabel: UILabel!
@IBOutlet weak var freeShippingButton: UIButton!
var viewModel: PaymentScreenViewModelProtocol
override func viewDidLoad() {
super.viewDidLoad()
priceLabel.text = viewModel.regularPriceString
discountedPriceLabel.text = viewModel.discountedPriceString
freeShippingButton.isHidden = !viewModel.isFreeShippingAvailable
}
@IBAction func userDidPressFreeShippingButton() {
viewModel.userSelectedFreeShipping()
}
@IBAction func userDidPressBuy() {
viewModel.buy()
}
}
这种方法使您可以将逻辑与视图分离。测试此逻辑也更容易。
要考虑和决定的一件事是关于如何将视图模型注入视图控制器的方法。我可以看到三种可能性:
init
-您提供了一个自定义初始化程序,需要传递视图模型。这意味着您将无法使用segue
或storyboards
(您将能够使用xib
)。这将使您的视图模型成为非可选。prepareForSegue
中)。这样一来,您就可以使用segue
,storyboard
,并且视图模型是非可选的(这只会增加额外的空实现的开销)。