Ключом к цели вашего конечного автомата является то, что у вас есть объекты интерфейса, которые вы хотите включить или отключить в зависимости от состояния.Это включение / отключение должно быть задачей контроллера представления.Само состояние является просто основой для ответов на такие вопросы, как «Какова текущая ситуация» и «Что должно произойти дальше».
Вот короткий простой пример конечного автомата, который иллюстрирует.Это намеренно тривиально.У нас всего две кнопки и всего два состояния;в каждом состоянии должна быть включена ровно одна кнопка.Состояния представлены случаями перечисления, и мы используем наблюдателя-установщика для этого перечисления, чтобы отвечать при каждом изменении состояния.Перечисление инкапсулирует логику того, сколько существует состояний и каково следующее состояние, в то время как контроллер представления является посредником между изменением состояния и изменением интерфейса:
class ViewController: UIViewController {
@IBOutlet weak var takePictureButton: UIButton!
@IBOutlet weak var deletePictureButton: UIButton!
@IBOutlet weak var pictureImageView: UIImageView! // not used in the example
@IBAction func doTakePicture(_ sender: Any) {
// do stuff
doNextState()
}
@IBAction func doDeletePicture(_ sender: Any) {
// do stuff
doNextState()
}
enum State {
case pictureNotTaken
case pictureTaken
var nextState : State {
switch self {
case .pictureNotTaken:
return .pictureTaken
case .pictureTaken:
return .pictureNotTaken
}
}
}
var state : State = .pictureNotTaken {
didSet {
updateInterface()
}
}
func doNextState() {
self.state = self.state.nextState // triggers the setter observer
}
func updateInterface() {
switch state {
case .pictureNotTaken:
takePictureButton.isEnabled = true
deletePictureButton.isEnabled = false
case .pictureTaken:
takePictureButton.isEnabled = false
deletePictureButton.isEnabled = true
}
}
}
Вероятно, вам нужно некоторое расширение этого шаблона.
единственный способ, которым я решил обновить пользовательский интерфейс, - передать ссылку на представление представления конечному автомату
Это то, что делает вышеупомянутый шаблон не до.Наблюдатель-установщик решает эту проблему для нас.
Теперь вы можете возразить, что оператор switch
в updateInterface
выполняет неправильную работу.Он определяет, как интерфейс полностью отражает состояние в контроллере представления.Ваш импульс состоит в том, чтобы сказать, что эти знания, безусловно, являются частью состояния (и именно поэтому вы создали свой код так, как вы это сделали).
Мой ответ будет: ну, да, и нет,Иногда я так чувствую, и способ решения проблемы состоит в том, чтобы наделить конечный автомат свойствами, выражающими все вопросы, которые могут возникнуть у контроллера представления о том, что текущее состояние означает в отношении интерфейса.Таким образом, знание перемещается в состояние, но интерфейс все еще корректно управляется контроллером представления.
Так, например, мы могли быдобавьте эти два свойства в наше перечисление State:
enum State {
// ... everything else is as before ...
var userCanTakePicture : Bool { return self == .pictureNotTaken }
var userCanDeletePicture : Bool { return self == .pictureTaken }
}
Итак, теперь нашему updateInterface
не нужно никаких специальных знаний о том, что означает каждое состояние;он просто спрашивает состояние, каким должен быть интерфейс , что проще и дает, возможно, более удовлетворительное разделение полномочий:
func updateInterface() {
self.takePictureButton.isEnabled = state.userCanTakePicture
self.deletePictureButton.isEnabled = state.userCanDeletePicture
}