Открытие ViewController в AppDelagate с сохранением вкладки
В моем проекте XCode, когда пользователь нажимает на уведомление, я хочу сначала отправить их определенному элементу в моем tabBar, затем я хочу создать экземпляр контроллера представления и отправить объект этому контроллеру представления. У меня есть код, который отправляет их в tabBar, я хочу, но я не знаю, как создать их экземпляр для контроллера представления, сохраняя tabBar и панель навигации подключенными к контроллеру представления. Все ответы на это требуют, чтобы вы изменили корневой контроллер представления, и это заставляет меня терять соединение с моей панелью вкладок и панелью навигации, когда вызывается контроллер представления.
Пример из реальной жизни: пользователь получает уведомление из Instagram, в котором говорится, что "Джон начал подписываться на вас" -> пользователь нажимает на уведомление -> Instagram открывает и показывает вкладку уведомлений -> быстро отправляет пользователя в профиль "Джон" и, когда пользователь нажимает кнопку "Назад" отправляет их обратно на вкладку уведомлений
Должен знать: причина, по которой я иду на определенную вкладку в первую очередь, заключается в том, чтобы получить контроллер навигации этой вкладки, потому что у контроллера представления, который я собираюсь, его нет.
Вот мой рабочий код по отправке пользователя на вкладку "Уведомления" (я добавил комментарии, чтобы вести себя как пример в Instagram для лучшего понимания):
if let tabbarController = self.window!.rootViewController as? UITabBarController {
tabbarController.selectedViewController = tabbarController.viewControllers?[3] //goes to notifications tab
if type == "follow" { //someone started following current user
//send to user's profile and send the user's id so the app can find all the information of the user
}
}
3 ответа
Прежде всего, вам нужно выделить TabBarController:
let storyboard = UIStoryboard.init(name: "YourStoryboardName", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "YourTabBarController") as! UITabBarController
А потом навязать все viewControllers
TabBarController. Если ваши viewControllers встроены в UINavigationController
? Если это так, то вместо этого вам понадобится навигационный контроллер:
let first = storyboard.instantiateViewiController(withIdentifier: "YourFirstNavigationController") as! UINavigationController
let second = storyboard.instantiateViewiController(withIdentifier: "YourSecondNavigationController") as! UINavigationController
let third = storyboard.instantiateViewiController(withIdentifier: "YourThirdNavigationController") as! UINavigationController
Также вам следует создать экземпляр желаемого ViewController:
let desiredVC = storyboard.instantiateViewController(withIdentifier: "desiredVC") as! ExampleDesiredViewController
Сделайте все NavigationControllers как viewControllers
TabBarController:
tabBarController.viewControllers = [first, second, third]
И проверьте: это по вашему выбору.
if tabBarController.selectedViewController == first {
// Option 1: If you want to present
first.present(desiredVC, animated: true, completion: nil)
// Option 2: If you want to push
first.pushViewController(desiredVC, animated. true)
}
Сделать tabBarController как rootViewController
:
self.window = UIWindow.init(frame: UIScreen.main.bounds)
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
Наконец: это ваш законченный код:
func openViewController() {
let storyboard = UIStoryboard.init(name: "YourStoryboardName", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "YourTabBarController") as! UITabBarController
let first = storyboard.instantiateViewiController(withIdentifier: "YourFirstNavigationController") as! UINavigationController
let second = storyboard.instantiateViewiController(withIdentifier: "YourSecondNavigationController") as! UINavigationController
let third = storyboard.instantiateViewiController(withIdentifier: "YourThirdNavigationController") as! UINavigationController
let desiredVC = storyboard.instantiateViewController(withIdentifier: "desiredVC") as! ExampleDesiredViewController
tabBarController.viewControllers = [first, second, third]
if tabBarController.selectedViewController == first {
// Option 1: If you want to present
first.present(desiredVC, animated: true, completion: nil)
// Option 2: If you want to push
first.pushViewController(desiredVC, animated. true)
}
self.window = UIWindow.init(frame: UIScreen.main.bounds)
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
}
Если вы хотите представить или нажать ViewController, когда уведомление постукивает? Попробуйте что-то подобное:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
switch response.actionIdentifier {
case UNNotificationDefaultActionIdentifier:
openViewController()
completionHandler()
default:
break;
}
}
}
Я могу придумать два способа сделать это:
1) Если этот контроллер представления является UINavigationController, вы можете просто отправить профиль из любого места:
if let tabNavigationController = tabbarController.viewControllers?[3] as? UINavigationController {
tabbarController.selectedViewController = tabNavigationController
let profileViewController = ProfileViewController(...)
// ... set up the profile by setting the user id or whatever you need to do ...
tabNavigationController.push(profileViewController, animated: true) // animated or not, your choice ;)
}
2) В качестве альтернативы мне нравится управлять такими вещами непосредственно из моего подкласса контроллера представления (в данном случае, PostListViewController). У меня есть этот вспомогательный метод в быстром файле, который я включаю во все мои проекты:
extension UIViewController {
var containedViewController: UIViewController {
if let navController = self as? UINavigationController, let first = navController.viewControllers.first {
return first
}
return self
}
}
Тогда я бы сделал это, чтобы выдвинуть новый контроллер представления:
if let tabViewController = tabbarController.selectedViewController {
tabbarController.selectedViewController = tabViewController
if let postListViewController = tabViewController.containedViewController as? PostListViewController {
postListViewController.goToProfile(for: user) // you need to get the user reference from somewhere first
}
}
В моем последнем живом проекте я использую тот же подход, что и ваш. Поэтому, хотя я сомневаюсь, что этот метод является правильным или идеальным для обработки push-уведомлений из AppDelegate (у меня все еще есть много вещей для изучения в iOS), я все еще делюсь им, потому что он работал для меня, и я считаю, что код все еще читаемый и довольно чистый.
Ключ должен знать уровни или стеки ваших экранов. Что такое childViewControllers, экран topMost, экран внизу и т. Д.
Тогда, если вы теперь готовы перейти к определенному экрану, вам, конечно, понадобится навигационный контроллер текущего экрана, на котором вы находитесь.
Например, этот блок кода взят из AppDelegate моего проекта:
func handleDeeplinkedJobId(_ jobIdInt: Int) {
// Check if user is in Auth or in Jobs
if let currentRootViewController = UIApplication.shared.keyWindow!.rootViewController,
let presentedViewController = currentRootViewController.presentedViewController {
if presentedViewController is BaseTabBarController {
if let baseTabBarController = presentedViewController as? BaseTabBarController,
let tabIndex = TabIndex(rawValue: baseTabBarController.selectedIndex) {
switch tabIndex {
case .jobsTab:
....
....
if let jobsTabNavCon = baseTabBarController.viewControllers?.first,
let firstScreen = jobsTabNavCon.childViewControllers.first,
let topMostScreen = jobsTabNavCon.childViewControllers.last {
...
...
Итак, как вы можете видеть, я знаю иерархию экранов, и, используя эти знания, а также немного терпения при проверке, нахожусь ли я на правильном экране с помощью точек останова и printobject (po)
Я получаю правильную ссылку. Наконец, в приведенном выше коде у меня есть topMostScreen
ссылка, и я могу использовать навигационный контроллер этого экрана, чтобы нажать на новый экран, если я хочу.
Надеюсь это поможет!