Present view controller modally at app launch - ios

My application has a setup screen that should be presented modally on the root view controller if certain conditions are met.
I have looked around on SO and the internet and the closest answer so far to how to go about doing this is here:
AppDelegate, rootViewController and presentViewController
There are 2 problems with this approach however:
In iOS 8, doing it this way makes a log appear in the console, which doesn't seem to be an error, but is probably not good nonetheless:
Unbalanced calls to begin/end appearance transitions for UITabBarController: 0x7fe20058d570.
The root view controller actually shows up very briefly when the app launches, and then fades into the presented view controller (even though I explicitly call animated:NO on my presentViewController method).
I understand that I can set my root controller dynamically in applicationDidFinishLaunchingWithOptions: but I specifically want to present the setup screen modally, so that when the user is done with it, it dismisses and the true first view of the application is revealed. This is to say, I don't want to dynamically change my root view controller to my setup screen, and present my app experience modally when the user is done setting up.
Presenting the view controller on my root view controller viewDidLoad method also leads to a noticeable blink of the UI when the app is launched for the first time.
Is it possible to programmatically present a view controller modally, before the application has rendered anything so that the first view in place is the modal view controller?
UPDATE: Thank you for the comments, adding my current code as suggested:
In my AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
[self.window makeKeyAndVisible];
[self.window.rootViewController presentViewController:[storyboard instantiateViewControllerWithIdentifier:#"setupViewController"] animated:NO completion:NULL];
return YES;
}
This does what I need except for the fact that it briefly shows the window's root view controller for a second when the application launches, then fades the setupViewController, which I find odd given that I am presenting it without animation and fading is not how a modal view controller is presented anyway.
The only thing that has gotten me close is manually adding the view in the root view controller's view did load method like so:
- (void)viewDidLoad
{
[self.view addSubview:setupViewController.view];
[self addChildViewController:setupViewController];
}
The problem with this approach is that I can no longer "natively" dismiss the setupViewController, and will now need to deal with the view hierarchy and animated it out myself, which is fine if it's the only solution, but I was hoping there was a sanctioned way of adding a view controller modally without animation before the root view controller displays.
UPDATE 2: After trying a lot of things out and waiting for an answer for 2 months, this question proposes the most creative solution:
iOS Present modal view controller on startup without flash
I guess it's time to accept that it's just not possible to present a view modally without animation before the root view controller appears. However the suggestion in that thread is to create an instance of your Launch Screen and leave that on for longer than default until the modal view controller has had a chance to present itself.

I guess it's time to accept that it's just not possible to present a view modally without animation before the root view controller appears.
Before it appears, no, you can't present. But there are multiple valid approaches to solve this visually. I recommend solution A below for its simplicity.
A. add launchScreen as subview, then present, then remove launchscreen
Solution is presented here by ullstrm and does not suffer from Unbalanced calls to begin/end appearance transitions:
let launchScreenView = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!.view!
launchScreenView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
launchScreenView.frame = window!.rootViewController!.view.bounds
window?.rootViewController?.view.addSubview(launchScreenView)
window?.makeKeyAndVisible()
// avoiding: Unbalanced calls to begin/end appearance transitions.
DispatchQueue.global().async {
DispatchQueue.main.async {
self.window?.rootViewController?.present(myViewControllerToPresent, animated: false, completion: {
launchScreenView.removeFromSuperview()
})
}
}
B. addChildViewController first, then remove, then present
Solution is presented here by Benedict Cohen.

I was in the same boat as you and found the same answer. I learned that you can get rid of the first problem (unbalanced calls warning) by setting the modalPresentationStyle of your setupViewController to .OverCurrentContext or .OverFullScreen. Problem solved - so I thought.
Only later I noticed the second problem and that was something I couldn't live with... back to square one.
As you, I wanted a solution with a normal view hierarchy and I didn't want to 'fake' something. I think the most elegant solution is switching your windows rootViewController on first dismissal of your setupViewController.
So, at launch, you set the setupViewController as the rootViewController (if needed):
var window: UIWindow?
var tabBarController: UITabBarController!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
tabBarController = window!.rootViewController as! UITabBarController
if needsToShowSetup() {
let setupViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SetupViewController") as! SetupViewController
window?.rootViewController = setupViewController
}
return true
}
When setup is done you call a method in your appDelegate to switch to the 'real' rootViewController:
func switchToTabBarController() {
let setupUpViewController = window!.rootViewController!
tabBarController.view.frame = window!.bounds
window!.insertSubview(tabBarController.view, atIndex: 0)
let height = setupUpViewController.view.bounds.size.height
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1, options: .allZeros, animations: { () -> Void in
setupUpViewController.view.transform = CGAffineTransformMakeTranslation(0, height)
}) { (completed) -> Void in
self.window!.rootViewController = self.tabBarController
}
}
I was after a 'cover vertical' dismiss animation. For crossfade and others, you could use UIView.transitionFromView(fromView: UIView, toView: UIView...). Hereafter you can present/dismiss your setupController the normal way, so your doneButton action could be something like this:
#IBAction func doneButtonSelected(sender: UIButton) {
if presentingViewController != nil {
presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
} else {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.switchToTabBarController()
}
}
Actually, I implemented this through delegation with the appDelegate being the delegate the first time around.

I understand that I can set my root controller dynamically in applicationDidFinishLaunchingWithOptions: but I specifically want to present the setup screen modally, so that when the user is done with it, it dismisses and the true first view of the application is revealed.
I have two suggestions. One is to try doing this in viewDidAppear:. I tried it and although you do see the root view controller's view if you look carefully, you barely see it, and sometimes you don't see it at all if you blink:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"setupViewController"] animated:NO completion:NULL];
}
Of course you'd need to add a flag so that you don't do that every time viewDidAppear: is called - otherwise you'll never be able to get back to this view controller at all! But that's trivial and I leave it as an exercise for the reader.
My other suggestion - and you have clearly thought about doing this - is to use a custom embedded (child) view controller instead. That works around the limitations of the whole "presentation" thing.
I'd launch and set up things dynamically, as you say, with the child view controller present if needed, configuring it all during the launch process. The child view controller's view would just cover the root view controller's view. So that's what the user would see as the app launches.
And then when the user's setup procedure is over and the user "dismisses" this view, you tear that view down, with animation, and remove the child view controller - revealing the root view controller's view underneath. The animation will make this all indistinguishable from the dismissal of presented view, even though it isn't really one.

Related

Present View Controller Over current tabBarController with NavigationController

When presenting or dismissing VC, I do not want to keep hiding and showing tabBar because it creates a poor user experience. Instead, I want present the next VC straight over the tab bar such that when I dismiss the nextVC by dragging slowly from left to right, I can see the tabBar hidden behind the view (As shown in image below)
Note, my app has two tabs with two VCs(VCA,VCB) associated to it. Both VC also have navigation bar embedded. VCA segues to VCA1 and VCB segues to VCB1. At the moment, inside VCA and VCB I am calling the following function to segue with some hiding and unhiding done when viewWillappear (Code below).
self.navigationController?.showViewController(vc, sender: self)
// Inside ViewWillAppear Only reappear the tab bar if we successfully enter Discover VC (To prevent drag back half way causing tab bar to cause comment entry to be floating). This code check if we have successfully enters DiscoverVC
if let tc = transitionCoordinator() {
if tc.initiallyInteractive() == true {
tc.notifyWhenInteractionEndsUsingBlock({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
if context.isCancelled() {
// do nothing!
}
else {
// not cancelled, do it
self.tabbarController.tabBar.hidden = false
}
})
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
----------Working solution from GOKUL-----------
Gokul's answer is close to spot on. I have played with his solution and came up with the following improvement to eliminate the need to have a redundant VC and also eliminate the initial VC being shown for a brief second before tabVC appears. But without Gokul, I would never ever come up with this!!
Additionally, Gokul's method would create a bug for me because even though I do have a initial "normal" VC as LoginVC before tabVC is shown. This loginVC is ONLY the rootVC if the user needs to login. So by setting the rootVC to tabVC in most cases, the navVC will never be registered.
The solution is to embed navigation controller and tabBar controller to one VC. But it ONLY works if the navVC is before the TabBarVC. I am not sure why but the only way that allowed me to have navVC-> tabVC-> VC1/VC2 is to embed VC1 with a navVC first than click on VC1 again to embed tabVC (It wouldn't allow me to insert one before tabVC and I also had to click the VC1 again after embedding the NavVC).
For your requirement we need to make some small changes in your given view hierarchy
Let me explain step by step,
To meet your requirement we have to add a UIViewController(let's say InitialVC) embedded with a UINavigationController and make it as initial viewcontroller.
Then add a UITabbarController with 2 VC (VCA,VCB) // IMPORTANT: Without any navigationcontroller embedded.
Add a segue between InitalVC and TabbarController with an unique identifier(ex: Initial)
In viewWillAppear of InitalVC perform segue as below (InitialVC is unnecessary to our design we are using this just to bridge navigationController and tabbarController).
self.performSegueWithIdentifier("Initial", sender: nil)
In TabbarControllerclass hide your back button, this ensures that InitialVC is unreachable.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
}
Now add a segue from a button between VCA and VCA1, thats it build and run you will see VCA1 presenting over VCA's tabbar.
What we have changed?
Instead of adding UINavigationController inside UITabbarController we have done vice versa. We can't directly add Tabbar inside navigation to do that we are using InitialVC between them.
Result:
1st way is create a image of the tabbar using UIGraphicsGetImageFromCurrentImageContext and set it on the bottom of the other view...
2nd way is show the next view in another new window that is above the tabbar, that way you wont need to hide the tabbar anymore, but seems like its in the navigation controller so this way doesnt seems available
Hiding and unhiding the tab bar is unnecessary. You only need to embed the UITabBarController inside the UINavigationController. That is, UINavigationController as the initial vc, UITabBarController as the root vc of UINavigationController.

Trigger viewWillAppear when using a transitioning delegate

I want to present a view controller:
Modally
Using custom transitions
At the same time, I want to make sure that when it is dismissed, the view controller behind it is aware of being pushed to the foreground.
My basic idea is to try some different configurations and see which one will lead to viewWillAppear being called on the view controller behind.
Attempt 1
presentedViewController.modalPresentationStyle = .Custom
presentedViewController.transitioningDelegate = someTransitioningDelegate
The results of this approach:
The custom transition works perfectly well
viewWillAppear does not get called on the view controller behind presentedViewController when I call presentedViewController.dismissViewControllerAnimated(true)
I do want viewWillAppear to be called on the view controller below the one being dismissed, so I did this:
Attempt 2
presentedViewController.modalPresentationStyle = .FullScreen
presentedViewController.transitioningDelegate = someTransitioningDelegate
or
presentedViewController.modalPresentationStyle = .FullScreen
presentedViewController.modalTransitionStyle = .CoverVertical
presentedViewController.transitioningDelegate = someTransitioningDelegate
The results of this approach:
viewWillAppear gets called on the view controller behind presentedViewController when dismissing presentedViewController
The transition occurs as expected when presenting the view controller.
When dismissing the view controller, the background during the transition is black, which is undesirable.
Seems that .FullScreen causes the view controllers behind presentedViewController to be removed from the display hierarchy - which is good because presumably that's what triggers the viewWillAppear call.
Attempt 3
presentedViewController.modalPresentationStyle = .FullScreen
presentedViewController.modalTransitionStyle = .CoverVertical
The results of this are:
viewWillAppear gets called on the view controller behind presentedViewController.
The background during the transition is the view controller located behind presentedViewController, which is desired.
No custom transition.
The project I'm working on is structured in a way that makes it difficult to use delegation (which seems to be the suggested answer here). Making use of NSNotificationCenter is another alternative which lets me call the code that is supposed to be called by viewWillAppear, but from attempt 3, I'm hoping there is a more elegant approach to achieve all these:
Trigger viewWillAppear
Use a custom transition
See the view controller being presented in the background during the transition animation
Seems Apple considers it foul play to invoke viewWillAppear etc., but it's okay to invoke beginAppearanceTransition and endAppearanceTransition, which in turn will invoke viewWillAppear. I'm going with this.
The way I have achieved this in the past is by calling viewWillAppear and viewDidAppear from the transition animator. Here's a simplified example:
public func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
fromVC.viewWillDisappear(true)
toVC.viewWillAppear(true)
UIView.animateWithDuration(0.3, animations: {
//Animations here
}, completion: { (success) in
toVC.viewDidAppear(success)
fromVC.viewDidDisappear(success)
transitionContext.completeTransition(true)
})
}
I call the "will" methods before I do animations, and the "did" methods after completion

UIWindow setRootViewController not clearing existing hierarchy

In my app I programmatically change root view controllers based on user actions e.g. login/logout functionality.
In iOS 8 - I'm noticing a strange issue. Even after setting rootViewController on the window, the old hierarchy still persists. I just verified it by capturing view hierarchy.
- (void) logout{
[self.window setRootViewController:[self loadLoginView]];
}
-(UIViewController *) loadLoginView{
WelcomeScreenVC *wsVC;
wsVC = [[WelcomeScreenVC alloc] initWithNibName:#"WelcomeScreenVC" bundle:nil];
UINavigationController *onboardingVC = [[UINavigationController alloc]initWithRootViewController:wsVC];
return onboardingVC;
}
Even after executing this line of code, the old logged in view hierarchy still persists. Would appreciate if anybody can suggest what's happening behind the scenes.
Edit: I just looked at UIWindow setRootViewController documentation and here's what Apple has to say about it:
The root view controller provides the content view of the window.
Assigning a view controller to this property (either programmatically
or using Interface Builder) installs the view controller’s view as the
content view of the window. If the window has an existing view
hierarchy, the old views are removed before the new ones are
installed.
I have noticed the very same thing.
Basically, I have a fairly complicated storyboard that acts as a login/welcome interface. This interface sits in a navigation controller, which presents another navigation controller modally.
After a certain point, the user takes an action that transitions him to the main interface. Using the iOS 8 view debugger I noticed that the old view hierarchy was still around after setting the rootViewController property of the window.
My solution, for now is to use the following code, right before I re-assing the window.rootViewController property:
for (UIView* subView in self.window.rootViewController.view.subviews) {
[subView removeFromSuperview];
}
[self.window.rootViewController.view removeFromSuperview];
It ain't pretty, but it works.
Another odd thing I noticed is that the welcome interface's modally presented viewController is not properly cleaned up using this method. I have to manually dismiss it AND do this clean up.
The best way to fix is:
self.window.subviews.forEach { $0.removeFromSuperview() }
or, in old style:
for view in self.window.subviews {
view.removeFromSuperview()
}
var loginNavigationController: OnBoardViewController?{
willSet{
if newValue == nil {
loginNavigationController?.view.removeFromSuperview()
}
}
}
loginNavigationController = nil
Then set window.rootviewcontroller = {Different VC}

Return to initial view controller when user logs out

I'm working on an app which uses Facebook integration, and the log in system works fine now. However, I can't seem to return to my initial view controller when the user clicks log out.
Here's an overview of my storyboard:
I would like to return to the start when the user clicks the blue button (on the top). What would I do to achieve that? As you can see I have multiple Navigation Controllers, and I only use Push-seguesto get there. However, I do use the SWRevealViewController, which you can see in the middle.
I've tried [self.navigationController popToRootViewControllerAnimated:YES]; which doesn't do anything.
Any advice? Anyone familiar with the SWRevealViewController and what it might have done to my Navigation stack? Any help will be appreciated!
Thanks.
Try this,
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"NameOfYourStoryBoard"
bundle:nil];
LoginViewController *add =
[storyboard instantiateViewControllerWithIdentifier:#"viewControllerIdentifier"];
[self presentViewController:add
animated:YES
completion:nil];
Write Below Method in root viewcontroller
- (IBAction)returnToDashboard:(UIStoryboardSegue *)segue;
Give segue connection to destination view controller like below
Give identifier to segue and assign method to that segue
use below method in destination view controller
[self performSegueWithIdentifier:#"pushtoDashboard" sender:self];
First of all, I don't think you need that many UINavigationControllers. Using only one in your application should be enough.
The reason popToRootViewController is not working in your case is because it will go to the first view controller withing a UINavigationController. You have nested many UINavigationControllers, thus when you click the blue button in the settings view controller it will go to the sidebar view controller (can't read it properly, the image is small).
You can do the following to get to the root view controller of your app:
UINavigationController *rootController =[(UINavigationController*)[(AppDelegate*)
[[UIApplication sharedApplication]delegate] window] rootViewController]];
Replace AppDelegate with however it's called in your app.
But my advice is to remove all intermediate UINavigationControllers. Then just doing popToRootViewController should do the trick.
Problem
You'd like to return from a view controller (source) to the start (a
destination view controller) when the user clicks the blue button (on the
top)
Recommendation
I recommend you take a quick look at the highly rated SO answer which demonstrates how to use the unwind segue (that's the little green exit box on your view controller in the storyboard). Unwind segues are the modern way of accomplishing your goal, but you can also call dismissViewController:animated from the source controller. You should also take a quick read of a very small Apple note (TN2298) on using the unwind segue.
Essentially you will want to add the following method to your destination view controller:
- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
{
}
Then use ctrl+drag and click from the blue button down to the green exit icon on the source view controller. This will popup a menu and you can select unwindToMainMenu from the list. You will need to give the new segue an identifier in the Identity Inspector (e.g. segueToMain).
Manual Unwind
The technical note above (TN2298) will also show you how you can create a manual unwind segues that may be called programmatically (similar to how one might say performSegueWithIdentifier...).
I was working on a very similar problem. I am using a storyboard with a navigation controller & implemented the highly recommended SWRevealViewController, with iOS 7 & Xcode 5.1. I tried unsuccessfully to implement some of the solutions mentions. Either the screen didn't change or I got blank table. I used a hybrid version of the programatic examples provided in SWRevealController & other answers here to get a working solution. I added this as apart of my login button action
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
InboxTableViewController *viewController = [storyBoard instantiateViewControllerWithIdentifier:#"inbox"];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
[self.revealViewController pushFrontViewController:nav animated:YES];
I retrieved my storyboard & initiated the view controller I wanted from the storyboard & then added to a navigation controller. Finally I used the SWRevealViewController method to Push the view I desired to the front.
I'm using swift and what worked for me was this:
var loginVC: UIViewController? = self.storyboard?.instantiateViewControllerWithIdentifier("UILogin") as? UIViewController
self.presentViewController(loginVC!, animated: true, completion: nil)
When you have different storyboards simply "presenting" the required VC from the initial storyboard does the trick:
Swift 3
if let loginVC = UIStoryboard(name: "Login", bundle: nil).instantiateInitialViewController() {
present(loginVC, animated: true, completion: nil)
}
In some cases there might be leaking UITransitionView's. You might remove them right after the "presenting" code, but not before it and not in it's completion:
if let subviews = UIApplication.shared.keyWindow?.subviews,
let transitionViewClass = NSClassFromString("UITransitionView") {
for subview in subviews where subview.isKind(of: transitionViewClass) {
subview.removeFromSuperview()
}
}
<– This works as of Xcode 8.2.1 and iOS 10.2 but no guarantee if will work forever. Be careful with it.

How to dismiss view controllers at any time (even during transitions) or when it is safe to dismiss a view controller ?

I have an iOS app that has a connection to a server. If we get disconnected, I want to be able to dismiss the top view controllers to get back to a "connecting to server" view controller. The problem is that a disconnection can occur at any time, including during a transition between view controllers.
The view controller hierarchy is like so:
ConnectingToServerViewController
SignInViewController
MainAppViewController
Other view controllers
When a disconnection is detected I want the view hierarchy to collapse back to:
ConnectingToServerViewController
So when a disconnection is detected, this method is called on the ConnectingToServerViewController to dismiss anything that it has presented and go back to attempting to connect to server:
- (void)restartSession
{
if (self.presentedViewController) {
[self dismissViewControllerAnimated:NO completion:nil];
}
}
However, if I try to dismiss while a view transition is occurring, I get errors such as
*** Assertion failure in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:], /SourceCache/UIKit/UIKit-2380.17/UIWindowController.m:211
attempt to dismiss modal view controller whose view does not currently appear. self = <YYYYYViewController: 0x2089c8a0> modalViewController = <XXXXXViewController: 0x208e6610>
attempt to dismiss modal view controller whose view does not currently appear. self = <WWWWWWViewController: 0x1fd9e990> modalViewController = <YYYYYViewController: 0x2089c8a0>
The first of which will crash the app, the second will just not dismiss anything and continue to show the current presented view controller.
Thoughts:
delays won't work since we don't know when to start the delay
is there a way to track when view transitions complete?
should all view controllers override willAppear, didAppear and alert the app when it is safe to dismiss?
perhaps instead of dismiss, I should just set a new root view controller?
I've made sure that all overridden view(will|did)(dis)?appear methods call the appropriate super method.
Any solution that requires all view controllers to override view(did|will)appear methods to track state sounds like it could cause issues if we forget to set the base class for a new view controller.
Do something like this. Try this out once,
UIViewController *controller = self.presentingViewController; //THIS LINE IS IMP
[self dismissViewControllerAnimated:YES
completion:^{
[controller presentViewController:adminViewController animated:YES completion:nil];
adminViewController.view.superview.frame = CGRectMake(1024/2 - 400, 768/2 - 280, 800 , 560);//it's important to do this after
[adminViewController release];
}];
One way that has worked for me is to assign a new view controller to the root view controller. That way, views in the old hierarchy can animate and transition to their hearts content while we have new controllers.
eg
- (void)restartSession
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ConnectingToServerViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"ConnectingToServerViewController"];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[UIApplication sharedApplication].delegate.window.rootViewController = vc;
}
I'm not sure if I'm aware of all the downsides to this though. Perhaps the old view controllers will never get freed because of a dangling strong reference? We're no longer reusing ConnectingToServerViewController, we have to recreate that each time.
I based the code on what I saw in this answer for Managing and dismissing Multiple View Controllers in iOS.
It seems like you are trying to dismiss the view controller when it is not currently on screen. To check if it is on screen you could use:
if (self.presentedViewController.view.window)
{
[self dismissViewControllerAnimated:NO completion:nil];
}
else
{
self.presentedViewController = nil;
}
I will answer in order.
is there a way to track when view transitions complete?
You could try with the UINavigationControllerDelegate (if you are using one of those). Other approach could be using a custom animator.
should all view controllers override willAppear, didAppear and alert the app when it is safe to dismiss?
That's an option. You are free to do it if you want. Another option is not to do that. I think that container view controllers such as navigation controller has better approaches.
I should just set a new root view controller?
I would suggest to do the opposite. I would set the SignInViewController / MainAppViewController as the root flow, and present modally ConnectingToServerViewController on demand. In my opinion that's a healthier approach.
Hope it helps.

Resources