3D Touch, Peek и Pop сбой с фатальной ошибкой: неожиданно обнаружил ноль при развертывании необязательного значения
Редактировать:
У меня есть TableView с 2 ячейками, я пытаюсь реализовать Peek & Pop с 3DTouch.
- 2 ячейки перенаправляют на 2 разных View Controller через modalView
- Когда пользователь нажимает на ячейку на первом контроллере вида, я отправляю данные на контроллер второго вида
- И когда пользователь выбирает ячейку во втором контроллере представления, модальное представление отклоняется, и данные отправляются обратно в первый контроллер представления.
Мне удалось преобразовать каждую отдельную ячейку в Peek и повысить ее по сравнению с другими ячейками, но как только я хочу вывести ее и перенаправить на другой View Controller, мое приложение вылетает
Вот мой код для 1stVC: я зарегистрировался для предварительного просмотра в viewDidLoad:
if( traitCollection.forceTouchCapability == .available){
registerForPreviewing(with: self, sourceView: self.tableView)
}
Я тогда соответствую протоколу UIViewControllerPreviewingDelegate
реализуя 2 метода:
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let indexPath = tableView?.indexPathForRow(at: location) else { return nil }
previewingContext.sourceRect = tableTest.rectForRow(at: indexPath)
let sb = UIStoryboard(name: "Main", bundle: nil)
guard let detailVC = sb.instantiateViewController(withIdentifier: "DestinationViewController") as? DestinationViewController else { return nil }
return detailVC
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit :UIViewController) {
show(viewControllerToCommit, sender: self)
}
в func tableView(...didSelectRowAt)
:
if (indexPath.row == 0) {
guard let controller = storyboard?.instantiateViewController(withIdentifier: "DestinationViewController") as? DestinationViewController else { return }
controller.titlePassed = cell?.textLabel?.text
controller.variableIn2ndVC = theVariabletoSend
controller.anotherVarIn2nd = theVariabletoSend2
...
...
navigationController?.present(controller, animated: true, completion: nil)
}
Мой код для второго контроллера представления:
var titlePassed: String?
var variableIn2ndVC: String?
var anotherVarIn2nd: String?
...
...
@IBAction func cancelButton(_ sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
...
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 && titlePassed == "FROM" {
return 1
} else {
return Stations.count
}
}
public func numberOfSections(in tableView: UITableView) -> Int {
if titlePassed == "FROM" {
return 2
} else {
return 1
}
}
В didSelectRowAt
У меня есть обратный вызов, чтобы отправить некоторые данные на 1-й контроллер
Когда пользователь нажимает на ячейку, я беру текст в textLabel
и отправить его в 1-й ВК с помощью callBack
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 1 && titlePassed == "FROM" {
let stringToSendBack = cell?.textLabel!.text
// put str in callBack in order to access it in the 1st VC
callBack?(str!)
dismiss(animated: true, completion: nil)
}
1 ответ
Я думаю, что вы уже решили это, но я просто подведу итоги того, что вы уже сделали, просто для протокола.
Давайте на данный момент проигнорируем взгляд и популярность. Затем, когда вы обычно переходите от главного контроллера к подробному контроллеру (DestinationViewController) в ответ на нажатие пользователем ячейки, вы делаете это следующим образом (сокращенный код):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let controller = storyboard?.instantiateViewController(withIdentifier: "DestinationViewController") as? DestinationViewController else { return }
controller.titlePassed = cell?.textLabel?.text
controller.variableIn2ndVC = theVariabletoSend
controller.anotherVarIn2nd = theVariabletoSend2
navigationController?.present(controller, animated: true, completion: nil)
}
Все идет нормально. Теперь давайте взглянем и заглянем в картину. Это совершенно другая ситуация. Вы создаете навигационный контроллер следующим образом (снова сокращенно):
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
let sb = UIStoryboard(name: "Main", bundle: nil)
guard let detailVC = sb.instantiateViewController(withIdentifier: "DestinationViewController") as? DestinationViewController else { return nil }
return detailVC
}
Увидеть разницу?
В первом случае вы создаете DestinationViewController и настраиваете его.
Во втором случае вы создаете DestinationViewController, но вы полностью забыли настроить его. Таким образом, он ненастроен и все его свойства
nil
,
Этот факт каким-то образом настигает вас позже и приводит к краху.
Если вы хотите посмотреть, а затем всплыть, то есть представить DestinationViewController после 3D-касания, как если бы пользователь просто коснулся ячейки, вы должны сделать все, что сделали бы, если бы пользователь просто коснулся ячейки. Вы можете сделать это любым из двух методов UIViewControllerPreviewingDelegate, но вы обязательно должны сделать это во втором, если вы не сделали этого в первом, потому что это тот, который фактически представляет контроллер представления. В противном случае вы будете представлять неисправный контроллер вида (как вы уже знаете).
Итак, подведем итог, ваши проблемы вызваны предположением, что tableView(_:didSelectRowAt)
и два метода UIViewControllerPreviewingDelegate каким-то образом взаимосвязаны. Они не. Это две совершенно разные ситуации - пользователь нажал или пользователь нажал 3D. Вам нужны полные реализации создания и настройки контроллера представления для каждого из них.