The iOS Accessibility Handbook
A Closed Question Level is defined by a question and a set of possible answers, one or more being valid (for the sake of simplicity we will not implement error messages when the user answers are wrong, we will simply wait for one correct answer):
The Closed Question View Controller will simply display a Table View component, we go for a simple design and go with a minimal user interface (white text on black background) as we want to provide immersion for the player, he must learn the accessibility mechanisms, how to move the cursor on each possible answer by swiping left/right, and then validating it by performing the usual VoiceOver validation action (double tap):
class ClosedQuestionLevel: Level { typealias Question = String
var question: Question
init(question: Question, answers: [Answer], validAnswers: [Answer]) { self.question = question
super.init(answers: answers, validAnswers: validAnswers)
} }
class ClosedQuestionViewController: UIViewController, Coordinated { var coordinator: AppCoordinatorProtocol?
var mainLabel: UILabel = {
let label = UILabel() label.numberOfLines = 0 label.textColor = .white label.textAlignment = .center return label
}()
var tableView: UITableView = {
let tableView = UITableView() tableView.backgroundColor = .clear tableView.separatorStyle = .none return tableView
}()
// MARK: – Initialization
private var level: ClosedQuestionLevel!
convenience init(level: ClosedQuestionLevel) { self.init()
self.level = level }
// MARK: – View Lifecycle override func viewDidLoad() {
super.viewDidLoad()
setupViews()
self.mainLabel.text = level.question setupTableView()
}
private func setupViews() { self.view.addSubview(self.mainLabel) self.view.addSubview(self.tableView)
self.mainLabel.translatesAutoresizingMaskIntoConstraints = false self.mainLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true self.mainLabel.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 30).isActive = true self.mainLabel.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 150).isActive = true
self.tableView.translatesAutoresizingMaskIntoConstraints = false self.tableView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true self.tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 30).isActive = true self.tableView.topAnchor.constraint(equalTo: self.mainLabel.bottomAnchor, constant: 60).isActive = true self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -30).isActive = true
}
private func setupTableView() { self.tableView.dataSource = self self.tableView.delegate = self
} }
extension ClosedQuestionViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 70.0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.level.answers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell()
cell.backgroundColor = .clear
cell.textLabel?.text = self.level.answers[indexPath.row]
cell.textLabel?.textColor = .white
return cell }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let answer = self.level.answers[indexPath.row]
coordinator?.validate([answer])
} }
Whenever the user select a given answer, we ask to the app coordinator to check if it’s valid and if it is we route to the next level.
After each levels have been completed, the app coordinator, driven by the game flow, will present the chapter screen for the next game mode levels: the Open Question Levels.