Хорошая стратегия для замены частей функциональности в iOS ViewControllers
У меня есть ВК в приложении для iOS, которые имеют довольно много элементов управления пользовательским интерфейсом. Теперь мне нужно заменить или "смоделировать" некоторые из этих элементов управления, когда они находятся в определенном состоянии. В некоторых случаях это будет просто отключение действий кнопок, но в некоторых случаях выполняемые действия должны быть заменены чем-то совершенно другим.
Мне не очень нравится идея засорять подобные проверки по всей базе кода.
if condition {
...Special/disabled functionality
} else {
...Normal functionality
}
В Android я могу просто создать подкласс для каждого фрагмента / действия и создать там функциональность, а затем выполнить if / else при вставке фрагментов или запуске действий.
Но на iOS с Storyboards/IBActions и Segues, пользовательские интерфейсы и виртуальные каналы действительно тесно связаны. Вы либо заканчиваете тем, что дублируете представления пользовательского интерфейса, либо добавляете много привередливого кода к уже большим 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()
}
}
}
1 ответ
Я бы предложил использовать шаблон MVVM (Model - View - ViewModel). Вы передаете 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
s,storyboard
s и иметь модель представления не обязательно (это просто добавляет накладные расходы на наличие дополнительной пустой реализации). - Через настройку свойства без реализации по умолчанию - это в основном означает, что ваша модель представления должна быть необязательной, и вам придется проверять ее почти каждый раз, когда вы получаете к ней доступ.