英文:
Navigating UITabController from UIDelegate on Notification action
问题
以下是您要翻译的部分:
"I am using FCM for notifications, the app is configured and receiving the notifications correctly and I also know where to put the action related to the notification depending on the data sent in the notification.
The problem is I have no idea on how to make it redirect to the specific ViewController I am using when the notification is clicked.
My Initial View Controller is a regular ViewController of class AcessController that is used to either register or log in the user account.
Once logged in, I redirect the user to the main Tab Controller like this:
func loginExitoso()
{
self.performSegue(withIdentifier: "loginExSeg", sender: nil)
}
The segue is configured like this:
Inside my AppDelegate I have the following code for the notification action:
extension AppDelegate: UNUserNotificationCenterDelegate {
// other functions omitted
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse) async {
let userInfo = response.notification.request.content.userInfo
if userInfo["action_location"]! as? String == "horario" {
self.window?.rootViewController!.performSegue(withIdentifier: "gotoHusoHorarioSegue", sender: nil)
}
}
}
From what I understand the rootViewController in this case would be AccessController, but for some reason the segue thing does nothing, I even added a prepare function inside the AccessController and the segue is specifically connected to the correct screen.
What am I doing wrong?
Is there a way to do this using the UITabBarController because I only have five cases of navigation and they are all tabs from the main TabBar and I just need to pass arguments to one of them?
UPDATE
I tried doing what Petar commented but I wasn't able to solve this. I am going to give a little more information.
This is part of my storyboard. As you can see my initial ViewController is the one on the left and when login is successful, it redirects to the Main Nav Controller which has the tab navigation.
Next, I have a segue from there to the Navigation Controller of one of the sections of the app.
That is a relationship segue used for Navigation of the app. It is connected from the Main Nav Controller to the View Controller of that specific section and it has this ID:
I am not sure if there's a difference between relationship segue and others.
I updated the upper code like this:
let userInfo = response.notification.request.content.userInfo
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
if userInfo["action_location"]! as? String == "lunahoy" {
} else if userInfo["action_location"]! as? String == "mes" {
} else if userInfo["action_location"]! as? String == "anio" {
} else if userInfo["action_location"]! as? String == "horario" {
viewController.performSegue(withIdentifier: "gotoHusoHorarioSegue", sender: self)
} else if userInfo["action_location"]! as? String == "servicios" {
}
When running this I got this error:
Thread 1: "Receiver (<AppName.MainNavController: 0x107833400>) has no segue with identifier 'gotoHusoHorarioSegue'"
Also, I have an issue with the above code where my app's upper bar (time and stuff) goes black. It's normally white. This happens because of these lines, but from what I understand this is the only way to switch to that particular controller:
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
Also, one more requirement to the question. In a specific case I need to be able to send an additional parameter through the navigation, how would that work with a relationship segue? I basically need to initialize a variable in one of the View Controllers to select a specific entry. The rest of the sections don't require this.
Help would be greatly appreciated, because I am running out of ideas. I don't know if my app's structure is not adequate for this or I am seriously ignorant on Storboard navigation.
UPDATE 3
I was able to almost get this to work. I made this adjustment in AppDelegate.
let userInfo = response.notification.request.content.userInfo
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
guard let tabBarController = window?.rootViewController as? UITabBarController else {
return
}
if userInfo["action_location"]! as? String == "lunahoy" {
tabBarController.selectedIndex = 0
if let tabBarController = self.window?.rootViewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController as? LunaHoy {
selectedViewController.idEntrada = (userInfo["id_entrada"]! as? Int)!
}
} else if userInfo["action_location"]! as? String == "mes" {
tabBarController.selectedIndex = 1
} else if userInfo["action_location"]! as? String == "anio" {
tabBarController.selectedIndex = 2
} else if userInfo["action_location"]! as? String == "horario" {
tabBarController.selectedIndex = 3
} else if userInfo["action_location"]! as? String == "servicios" {
tabBarController.selectedIndex = 4
}
英文:
I am using FCM for notifications, the app is configured and receiving the notifications correctly and I also know where to put the action related to the notification depending on the data sent in the notification.
The problem is I have no idea on how to make it redirect to the specific ViewController I am using when the notification is clicked.
My Initial View Controller is a regular ViewController of class AcessController that is used to either register or log in the user account.
Once logged in, I redirect the user to the main Tab Controller like this:
func loginExitoso()
{
self.performSegue(withIdentifier: "loginExSeg", sender: nil)
}
The segue is configured like this:
Inside my AppDelegate I have the following code for the notification action:
extension AppDelegate: UNUserNotificationCenterDelegate {
// other functions omitted
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse) async {
let userInfo = response.notification.request.content.userInfo
if userInfo["action_location"]! as? String == "horario" {
self.window?.rootViewController!.performSegue(withIdentifier: "gotoHusoHorarioSegue", sender: nil)
}
}
}
From what I understand the rootViewController in this case would be AccessController, but for some reason the segue thing does nothing, I even added a prepare function inside the AccessController and the segue is specifically connected to the correct screen.
What am I doing wrong?
Is there a way to do this using the UITabBarController because I only have five cases of navigation and they are all tabs from the main TabBar and I just need to pass arguments to one of them?
UPDATE
I tried doing what Petar commented but I wasn't able to solve this. I am going to give a little more information.
This is part of my storyboard. As you can see my initial ViewController is the one on the left and when login is successful, it redirects to the Main Nav Controller which has the tab navigation.
Next, I have a segue from there to the Navigation Controller of one of the sections of the app.
That is a relationship segue used for Navigation of the app. It is connected from the Main Nav Controller to the View Controller of that specific section and it has this ID:
I am not sure if there's a difference between relationship segue and others.
I updated the upper code like this:
let userInfo = response.notification.request.content.userInfo
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
if userInfo["action_location"]! as? String == "lunahoy" {
} else if userInfo["action_location"]! as? String == "mes" {
} else if userInfo["action_location"]! as? String == "anio" {
} else if userInfo["action_location"]! as? String == "horario" {
viewController.performSegue(withIdentifier: "gotoHusoHorarioSegue", sender: self)
} else if userInfo["action_location"]! as? String == "servicios" {
}
When running this I got this error:
Thread 1: "Receiver (<AppName.MainNavController: 0x107833400>) has no segue with identifier 'gotoHusoHorarioSegue'"
Also, I have an issue with the above code where my app's upper bar (time and stuff) goes black. It's normally white. This happens because of these lines, but from what I understand this is the only way to switch to that particular controller:
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
Also, one more requirement to the question. In a specific case I need to be able to send an additional parameter through the navigation, how would that work with a relationship segue? I basically need to initialize a variable in one of the View Controllers to select a specific entry. The rest of the sections don't require this.
Help would be greatly appreciated, because I am running out of ideas. I don't know if my app's structure is not adequate for this or I am seriously ignorant on Storboard navigation.
UPDATE 3
I was able to almost get this to work. I made this adjustment in AppDelegate.
let userInfo = response.notification.request.content.userInfo
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
guard let tabBarController = window?.rootViewController as? UITabBarController else {
return
}
if userInfo["action_location"]! as? String == "lunahoy" {
tabBarController.selectedIndex = 0
if let tabBarController = self.window?.rootViewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController as? LunaHoy {
selectedViewController.idEntrada = (userInfo["id_entrada"]! as? Int)!
}
} else if userInfo["action_location"]! as? String == "mes" {
tabBarController.selectedIndex = 1
} else if userInfo["action_location"]! as? String == "anio" {
tabBarController.selectedIndex = 2
} else if userInfo["action_location"]! as? String == "horario" {
tabBarController.selectedIndex = 3
} else if userInfo["action_location"]! as? String == "servicios" {
tabBarController.selectedIndex = 4
}
This works correctly, I am just not sure how the execution of selectedViewController.idEntrada
works, because it currently does nothing. Is this executed after viewDidAppear
. How can I refresh this information? The idEntrada
is used in my viewDidAppear
function on that Controller and it works, but not when it comes from here. Any recommendations?
答案1
得分: 1
你说得对,窗口的rootViewController是AccessController,但似乎还有另一个视图控制器(你的Tab Controller),它要么被推送到堆栈上,要么以模态方式呈现。
所以当你执行你的segue gotoHusoHorarioSegue
作为对通知的响应时,它必须由当前显示的(堆栈顶部的)视图控制器处理 - 我认为这应该是你的Tab Controller。也就是说,很可能你需要从Tab Controller 切换到下一个视图控制器。
更新:继续来自更新的问题
所以确实有一个关系和操作segue之间的区别。调用performSegue“从当前视图控制器的故事板文件中启动带有指定标识符的segue”。你的是一个关系segue,它不由标识符指定。它们用于在Storyboard中“静态”地布局UI组件。
因此,你可以在UITabBarController实例中使用设置具体的选项卡。
设置此属性会将所选的视图控制器更改为视图控制器数组中指定索引处的视图控制器
至于你的第二点:尽管更改窗口的根视图有效,但这可能不是你要采取的最佳方法。如果以后要带来登录流程(例如用户注销),这可能会导致进一步的问题 - 你必须保持可见视图控制器的状态,这种解决方案可能迅速转变为难以管理和容易出错的混乱。我建议使用UINavigationController作为你的应用程序的根(初始视图控制器),然后可以编程方式安排推送(显示)segue到你要导航到的各种其他视图控制器;推送(显示)segue可以通过编程方式执行。另外,一个不同但相当受欢迎的用户体验是将登录流程作为模态呈现,并在完成后解除它,从而显示其下的选项卡栏。
更新2:从更新的问题中继续
在某些情况下,我需要能够通过导航发送附加参数,使用关系segue如何实现这一点?基本上,我需要在一个View Controller中初始化一个变量以选择特定的条目。其他部分不需要这个。
如上所述,你无法使用关系segue来实现这一点。你可以直接将属性注入到视图控制器实例中。
英文:
You are right that the rootViewController of the window is the AccessController, but seems like there is another view controller (your Tab Controller) which is either pushed onto the stack or presented modally.
So when you perform your segue gotoHusoHorarioSegue
as a response to a notification it must be handled from the currently displayed (top of the stack) view controller - I believe this should be your Tab Controller. That said most probably you need to segue from the Tab Controller to your next VC .
UPDATE: Contd. from updated question
So there is indeed a difference between relationship and action segues. Calling performSegue Initiates the segue with the specified identifier from the current view controller's storyboard file.
Yours is a relationship segue which isn't specified by an identifier. They are used so that you can lay out statically
your UI components within the Storyboard.
So instead, you can use set the the concrete tab in your UITabBarController instance.
> Setting this property changes the selected view controller to the one
> at the designated index in the viewControllers array
To your second point: Even though changing the root of the window works, it is probably not the best approach you want to take. This could cause further problems if say you want to bring the login flow at a later point(user logs out for instance) - you'll have to keep the state of the visible vc and this kind of solution could quickly transition to unmanageable bug-prone mess. I'd suggest to use UINavigationController. as the root(the initial vc) of your app and then you can arrange your push(show) segues to the various other vcs that you want to navigate to; the push(show) segues could be executed programatically. Also a different but quite popular UX would be to bring up the login flow as a modal presentation and dismiss it once its finished thus revealing the tabbar beneath
it.
Update 2: Contd. from updated question
> In a specific case I need to be able to send an additional parameter
> through the navigation, how would that work with a relationship segue?
> I basically need to initialize a variable in one of the View
> Controllers to select a specific entry. The rest of the sections don't
> require this.
As stated above you cannot do that with a relationship segue. You can inject your properties directly to the view controller instance.
答案2
得分: 0
以下是翻译好的部分:
You can create an instance of the view controller with the following code.
let navVC = tabBarController.viewControllers![0] as! UINavigationController
var lunaHoy = navVC.topViewController as! LunaHoy
Then the problem with the parameter could be that it firstly needs to be casted as a string and then as an integer.
This could be because when obtaining it from the hash, you may be getting a String pointer, which can be cast to string, but, for it to be cast as an integer, it firstly needs to be converted to string to become a value, instead of a pointer. And finally you can pass it to the viewcontroller as an attribute.
let idEntradaString = userInfo["id_entrada"]! as? String
let idEntradaInt = Int(idEntradaString!)
lunaHoy.idEntrada = idEntradaInt!
tabBarController.selectedIndex = 0
You have to run tabBarController.selectedIndex = 0
in the end, so that when the change occurs, the variable is already initialized.
英文:
You can create an instance of the view controller with the following code.
let navVC = tabBarController.viewControllers![0] as! UINavigationController
var lunaHoy = navVC.topViewController as! LunaHoy
Then the problem with the parameter could be that it firstly needs to be casted as a string and then as an integer.
This could be because when obtaining it from the hash, you may be getting a String pointer, which can be cast to string, but, for it to be cast as an integer, it firstly needs to be converted to string to become a value, instead of a pointer. And finally you can pass it to the viewcontroller as an attribute.
let idEntradaString = userInfo["id_entrada"]! as? String            
let idEntradaInt = Int(idEntradaString!)           
lunaHoy.idEntrada = idEntradaInt!                       
tabBarController.selectedIndex = 0
You have to run tabBarController.selectedIndeex = 0 in the end, so that when the change occurs, the variable is already initialized.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论