Хорошая стратегия для замены частей функциональности в iOS ViewControllers - PullRequest
0 голосов
/ 06 сентября 2018

В приложении iOS есть венчурные капиталисты, которые имеют довольно много элементов управления пользовательским интерфейсом. Теперь мне нужно заменить или «смоделировать» некоторые из этих элементов управления, когда они находятся в определенном состоянии. В некоторых случаях это будет просто отключение действий кнопок, но в некоторых случаях выполняемые действия должны быть заменены чем-то совершенно другим.

Мне не очень нравится идея засорять подобные проверки вокруг кодовой базы.

if condition {
  ...Special/disabled functionality
} else {
  ...Normal functionality
}

В Android я могу просто создать подкласс для каждого фрагмента / действия и создать там функциональность, а затем выполнить if / else при вставке фрагментов или запуске действий.

Но на iOS с раскадровками / 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 Ответ

0 голосов
/ 06 сентября 2018

Я бы предложил использовать шаблон 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()
    }
}

Этот подход позволяет вам отделить вашу логику от ваших взглядов. Эту логику также проще проверить.
Одна вещь, которую нужно рассмотреть и решить, - это подход к внедрению модели представления в контроллер представления. Я вижу три возможности:

  1. Через init - вы предоставляете собственный инициализатор, требующий передачи модели представления. Это будет означать, что вы не сможете использовать segue или storyboards (вы сможете использовать xib с). Это позволит сделать вашу модель представления необязательной.
  2. Через настройку свойства с реализацией по умолчанию - если вы предоставляете некоторую форму реализации по умолчанию / пустую для вашей модели представления, вы можете использовать ее в качестве значения по умолчанию для нее и установить правильную реализацию позже (например, в prepareForSegue). Это позволяет вам использовать segue s, storyboard s и иметь необязательную модель представления (это просто добавляет накладные расходы на наличие дополнительной пустой реализации).
  3. Через установку свойства без реализации по умолчанию - это в основном означает, что ваша модель представления должна быть необязательной, и вам придется проверять ее почти каждый раз, когда вы получаете к ней доступ.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...