Closed Arkezis closed 7 years ago
I'll be back later today and can help you out :)
Hey, so to do what you are looking for is relatively easy. In your AppDelegate, you can instantiate a SnackbarController
and set the rootViewController
to your UITabBarController. For example:
import UIKit
import Material
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func applicationDidFinishLaunching(_ application: UIApplication) {
window = UIWindow(frame: Screen.bounds)
window!.rootViewController = SnackbarController(rootViewController: MyTabBarController())
window!.makeKeyAndVisible()
}
}
If you are using storyboards, you will want to do something like this:
import UIKit
import Material
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func applicationDidFinishLaunching(_ application: UIApplication) {
window = UIWindow(frame: Screen.bounds)
window!.rootViewController = SnackbarController(rootViewController: UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MyTabBarController"))
window!.makeKeyAndVisible()
}
}
Once that is all working, you can access the SnackbarController
with the optional UIViewController
property snackbarController?.snackbar
.
For your initial view controller, be mindful that you will probably need to access the snackbarController?.snackbar
property from the viewWillAppear
method because the viewDidLoad
method will not have access because the MyTabBarController
won't be connected to the SnackbarController
just yet.
You can look at this Programmatic SnackbarController Sample for a programmatic approach, and this Storyboard SnackbarController Sample for a storyboards approach.
Hope that helps and feel free to reopen or create a new issue if you need any further help :)
Ok, it's fine now, I didn't really understood how I can handle it with all my storyboards. The main info here is that the root must be a SnackbarController :)
Thanks for your help !
You can place the SnackbarController at any layer you like, and in this case, I think it makes most sense to be the lowest layer at the window's rootViewController. In some cases you may even want to add the SnackbarController as the base of each tab. So feel free to explore combinations, the SnackbarController is designed to fit anywhere based on needs.
Hi, sorry for hijacking this thread, but, it's relevant to my issue. I have a similar setup where I have a UITabBarController and I add that as the rootVC of my AppSnackbarController in appDidFinishLaunching. This is all working fine. However, if I navigate to one of my UITabBarController children, and then navigate to a child of that VC via a segue in my Storyboard, my snackbarController property is no longer visible to the child VC. Can you tell me how I make the snackbarController visible to VCs at lower levels in my app?
@codemodouk are you trying to access the Snackbar
through the viewDidLoad
method? If so, it may not be accessible because the view controller has not been added to the hierarchy at that time. Try the viewWillAppear
method, and use the optional snackbarController
property found as a UIViewController extension.
I will double check when I'm in front of my desktop, but If I recall, I'm calling this long after the viewDidLoad event.
I take it from your response that I should be able to get a handle to the snackbar controller object on all child navigation controllers of the UITabBarController. Is that correct?
Sent from my iPhone
On 9 Mar 2017, at 18:34, Daniel Dahan notifications@github.com wrote:
@codemodouk are you trying to access the Snackbar through the viewDidLoad method? If so, it may not be accessible because the view controller has not been added to the hierarchy at that time. Try the viewWillAppear method, and use the optional snackbarController property found as a UIViewController extension.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
@codemodouk yes, you should be able to get a handle so long as the view controller is a child of the SnackbarController
in the view hierarchy. Keep me posted :)
@danieldahan I've tested this further and can provide some additional information.
I have a Storyboard setup as follows:
UITabBarController -> UINavigationController -> UIViewController_Tab1 -> UINavigationController -> UIViewControllerChild
My app delegate instantiates the UITabBarController from the Storyboard and makes this the rootController of my AppSnackbarController object.
If I show the snackbar in UIViewController_Tab1 this works perfectly.
If I navigate to UIViewControllerChild via a modal segue that was created in the Storyboard and attempt to show the snackbar, nothing is displayed.
Is that expected behaviour?
@danieldahan - I don't mean to pressure, but any thoughts on this?
No worries. Where are you accessing the snackBarController
property in the UIViewControllerChild
?
Yes, in UIViewControllerChild.
If you are accessing it in the viewDidLoad
it may not actually be connected yet, which means that the optional snackbarController
won't be available. So put that code in the viewWillAppear
and it should be accessible.
Let me know if that works for you. Otherwise, can you share your code in that view controller so I can see what is going on.
Please reopen the issue if you need :)
I've uploaded a sample project which demonstrates my setup. You will see that I'm accessing the snackbarController
from buttons in the view controllers, so the property should have been initialised.
Sorry to hassle, but, do you have any update on this at all? If you've got concerns about opening the zip, I understand, and would be happy to provide by another channel if that helps. Please let me know.
No worries, I am testing it again now.
So there are two issues here:
SackbarController
, which can be remedied by doing this:func animateSnackbar() {
guard let sc = self.snackbarController ?? presentingViewController?.snackbarController else {
return
}
_ = sc.animate(snackbar: .visible, delay: 1)
_ = sc.animate(snackbar: .hidden, delay: 4)
}
func showSnackbar(withText text: String) {
guard let snackbar = self.snackbarController?.snackbar ?? presentingViewController?.snackbarController?.snackbar else {
return
}
snackbar.text = text
animateSnackbar()
}
You will see that I am using the presentingViewController
to access the snackbarController
if it doesn't find it in the current view controller.
snackbarController
itself, so you will need to update your storyboard. The other option is to wrap the presented UINavigationController
with another SnackbarController
, and you will be able to reuse the same code in your extension. I recommend this one.
Let me know what you choose and if you need, please reopen the issue. Thank you!
Thanks for looking into this for me. The approach you suggest of wrapping the UINavigationController
in a SnackbarController
I am doing elsewhere in my app when I present the childVC from a parentVC that implements the UIPopoverPresentationDelegate protocol.
However, in general, I'm trying to use the Storyboard where possible. I've attempted your recommended approach in the prepare(for segue: sender)
, but if I wrap the segue.destination
(A UINavigationController) in a new SnackbarController
, I get the error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <SnackbarTest.GreenViewController: 0x7fdc4540a0a0>.'
I also tried subclassing UINavigationController
to create a SnackbarNavigationController
. However, this complains too because the snackbarController
isn't open, so I get the error:
Overriding non-open var outside of it's defining module.
Have I misunderstood your suggestion? Sorry to be a noob pain, I would really like to make use of this feature, but keep hitting brick walls.
Hey, no worries, trying is how you learn. You should in your segue launch the SnackbarController
that wraps the UINavigationController
, and not have it do it from storyboards as well. Try that, and let me know.
You've totally thrown me now! Can you provide an example of how I can wrap the UINavigationController in the segue?
In the example project I uploaded, I added the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let navVC = segue.destination as? UINavigationController {
let _ = AppSnackbarController(rootViewController: navVC)
}
}
But I got this error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <SnackbarTest.GreenViewController: 0x7fdc4540a0a0>.'
Thanks once again for any help you can provide.
Seems like it can't find this GreenViewController
. Maybe you need to remove that reference as that is probably from the sample code. As for the code, that looks like the correct idea if not the correct way. I don't do much with storyboards. See if you can remove the GreenViewController
reference, and if you still have an issue, please let me know. Thank you!
Hey, thanks for the feedback I really appreciate it.
Sent from my iPhone
On 28 Mar 2017, at 21:17, Daniel Dahan notifications@github.com wrote:
Seems like it can't find this GreenViewController. Maybe you need to remove that reference as that is probably from the sample code. As for the code, that looks like the correct idea if not the correct way. I don't do much with storyboards. See if you can remove the GreenViewController reference, and if you still have an issue, please let me know. Thank you!
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
Hi @codemodouk,
After all, did you find a solution ? I have the same issue.
If anyone would like to send a sample over that reproduces this issue, I'd be happy to help. Thanks!
Hi @carbonimax, unfortunately, I haven't solved this yet by using Storyboards and segues. I can workaround it by conforming my source VC to UIPopoverPresentationControllerDelegate
and presenting my target VC as a popover. Using this approach, I can make use of UIPopoverPresentationControllerDelegate
function
func presentationController(UIPresentationController: UIModalPresentationStyle)
and wrap my targetVC in a SnackbarController
and make that the rootViewController of a UINavigationController
So something like this:
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
let navigationController = UINavigationController(rootViewController: AppSnackbarController(rootViewController: controller.presentedViewController))
return navigationController
}
func presentationController(UIPresentationController: UIModalPresentationStyle)
@danieldahan - I assume @carbonimax is struggling with the same issue I presented in the example project contained in the SnackbarTest.zip attached to this thread.
Hi @carbonimax, you must have given me some inspiration. Try this, I've just got it working:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let navVC = segue.destination as? UINavigationController {
let asc = AppSnackbarController(rootViewController: navVC.topViewController!)
navVC.viewControllers.insert(asc, at: 0)
}
}
Let me know if it works for you too.
Hi,
Thank you for your replies.
I found a working solution. I use a custom segue in IB with this class :
import Material
class SnackbarSegue: UIStoryboardSegue {
override func perform() {
let firstVCView = self.source.view as UIView!
let secondVCView = self.destination.view as UIView!
let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height
secondVCView?.frame = CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: screenHeight)
let window = UIApplication.shared.keyWindow
window?.insertSubview(secondVCView!, aboveSubview: firstVCView!)
self.source.present(SnackbarController(rootViewController: self.destination as UIViewController), animated: true, completion: nil)
}
}
I prefer your approach to mine as it keeps the implementation details outside of the view controller. I've modified it to suit my needs, so, thought I would share in case anyone else has a similar requirement:
class SnackbarSegue: UIStoryboardSegue {
override func perform() {
self.source.present(SnackbarController(rootViewController: self.destination as UIViewController), animated: true, completion: nil)
}
}
Hi,
I want to use your awesome SnackBar by using the SnackbarController but my main VC is a UITabBarController ! Can you help me on this ?
Moreover, do I need to instanciate my UITabBarController manually in AppDelegate ? (it's not the case for now ...)
Thanks a lot :)