I am using Map Kit View. mapViewWillStartLoadingMap method called when I navigate MapViewController first time. Then I go back previous controller and again navigate MapViewController , mapViewWillStartLoadingMap not called. Where is my mistake ?
I am using this method go to previous controller from MapViewController
dismissViewControllerAnimated(true, completion: nil)
Check that your ViewController containing the map is being deInitialised.
Add this to your VC and set a breakpoint on it.
deinit {
}
If its not that, check the delegate for the map view is set correctly when your map VC is on active.
Related
I am new to Swift programming. I am stuck in one scenario.
When my app loads, the first screen gets in ViewController1 user's latitude and longitude and then gets the data from a backend API and loads the data in a table view controller. This code of getting user's location and loading table view controller is in ViewDidLoad method. In this ViewController1, I have a button when tapped, I am presenting a view controller (Which is embedded in a navigation controller) modally. The following lines of code I am using to present this new ViewController (ViewController2).
let VC2 = self.storyboard?.instantiateViewControllerWithIdentifier("NewVC2") as! NewVC2
let navController = UINavigationController(rootViewController: VC2)
VC2.delegate = self
self.presentViewController(navController, animated: true, completion: nil)
When I close the ViewController2 by saying self.dismissViewController, it closes and when the control comes back to ViewController1, ViewDidLoad is executed again and all the code of invoking backend API and getting user's location is getting invoked.
So, for passing data back from ViewController2 on dismissing it, I used the delegate pattern. Created a protocol and defined a method in this protocol.
protocol ViewControllerClosedDelegate {
func isViewControllerClosed(closed: Bool);
}
defined the object of delegate in ViewController2.
var delegate: ViewControllerClosedDelegate! = nil
and added the following code while dismissing ViewController2.
delegate.isViewControllerClosed(true)
self.dismissViewControllerAnimated(true, completion: nil)
made the ViewController1 to implement this ViewControllerClosedDelegate and added the following function.
func isViewControllerClosed(closed: Bool) {
self.isFromClosedVC = closed
print(isFromClosedVC)
}
isFromClosedVC is a global variable defined ViewController1.
the value is printing correctly (true in this case) as being passed from ViewController2 when it is dismissed but when I try to print this in ViewDidLoad, it is being printed as false.
I thought of getting this value from ViewController2, and if it is true, not to execute the code to get users location and data from backend API.
Basically, I am trying to get the user's location and getting the data from backend API once and update my table view only on Swipe to refresh.
How do I achieve this? Please help.
Thank you.
Raj
Hey I am trying to make it so whenever the user presses the home button and then try to returns to my app, they are returned to the initial view controller. Is there anything I can write into the app delegate to get this to work?
I have tried to call this function in the applicationDidBecomeActive function:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewControllerWithIdentifier("homeVC") as! UIViewController
self.window?.rootViewController?.presentViewController(VC, animated: true, completion: nil)
But i get an error saying the view is not in the hierarchy.
Any help will be appreciated!
You want to implement the applicationDidBecomeActive method of your App's delegate, and programmatically perform a segue or dismiss the current view controller, depending on the navigation setup.
Your users might not like forced navigation though. The general assumption is that they can resume where they left off.
Well, every time the app is closed applicationDidEnterBackground in your AppDelegate is called. You can use that to present your view controller.
What I want to achieve: segue from gameVC to mainmenuVC and get rid of the gameVC
When the app starts it first shows a main menu viewcontroller with a play button that segues to the gameviewcontroller. When the user taps on a menu button sprite the following function in the gameviewcontroller gets called and it segues back to the main menu:
func returnToMainMenu () {
//This works but does not deinit the vc
navController?.dismissViewControllerAnimated(true, completion: nil)
/* this does not do anything:
navController?.popViewControllerAnimated(true)
*/
}
This is probably not how it is done properly and I think that might be the problem, but I could not get to work otherwise because gameViewController.navigationViewController is nil.
This is how my storyboard looks:
This is how the memory usage looks when the app is running. Those spikes/steps occur whenever the gameviewcontroller is loaded. It seems to me that the problem is, that the gameviewcontroller does not de-initialize when returnToMainMenu() is called.
Also, this never gets executed:
deinit {
debugPrintln("GameViewController deinitialized")
}
update:
I deleted this
navController = self
and defined navController in returnToMainMenu like this:
let navController = view.window?.rootViewController as! UINavigationController
segue back to main menu still works but it still does not deinit the vc
Of course it will memory leak.
override func viewDidLoad() {
navController = self
}
You just gave yourself a reference to itself. Usually when your vc goes offscreen, the view hierarchy no longer holds the view so the view is deinited. You set a reference to itself so whatever you do, it will always hold itself in memory and will never deinit.
I figured it out after watching Lecture 8 of the stanford iOS8 course (at 14:23).
The problem was that I added a reference to the gameviewcontroller in my gamescene to call its returnToMainMenu() method from the scene. In order for its memory to be cleared all the references to the VC have to be set to nil.
I solved it by referring to the navigation controller directly from my scene like this:
(scene!.view!.window?.rootViewController as! UINavigationController).dismissViewControllerAnimated(false, completion: nil)
I'm presenting a modal view controller using a custom transition (by setting its modelPresentationStyle to UIModalPresentationCustom, providing a transitioning delegate, and UIViewControllerAnimatedTransitioning object).
In the presented view controller, I have an unwind segue hooked up to a button. The segue fires just fine; the IBAction method in the presenting view controller is called, and so is prepareForSegue in the presented view controller. However, the presented view controller is not dismissed, and the appropriate transitioning delegate method (animationControllerForDismissedController:) is not called.
If, however, I set the presented view controller's modalPresentationStyle to UIModalPresentationFullScreen (the default), the view controller is dismissed properly (this breaks my custom transition, though).
I'm at a complete loss at what to do here. I've looked through Apple's documentation, and didn't notice anything saying that one had to do special things with unwind segues when dealing with custom transitions.
I'm aware that I could call dismissViewControllerAnimated:completion: in the IBAction method of the presenting view controller, but I'd rather use that as a last resort, and get the unwind segue working the way it should (or at least know why it's not working :) ).
Any help would be much appreciated,
Thanks in advance
It seems that if you use UIModalPresentationCustom to present the controller with a custom transition manager, you also need to use a custom transition manager to dismiss it (which makes sense I guess, since you can do all kinds of weird stuff in the animator object and UIKit can't be sure that just dismissing the screen as usual will completely restore the original state - I just wish it told you that explicitly...).
Here's what I've done to fix this in my app:
override segueForUnwindingToViewController in the parent view controller (the one to which you're moving after the dismiss animation) and return an instance of your UIStoryboardSegue, either the one you've used for the original transition or a new separate class
if the unwind segue's target view controller is in a navigation hierarchy, then you need to override that method in the navigation controller instead
in the perform method call dismissViewControllerAnimated
the presented view controller needs to still hold a valid reference to the transitioning delegate, or you'll get an EXC_BAD_ACCESS (see DismissViewControllerAnimated EXC_Bad_ACCESS on true) - so either make it keep the delegate as a strong reference as described in that thread, or assign a new one before calling dismissViewControllerAnimated (it's possible that changing modelPresentationStyle to e.g. full screen before dismissing would work too, but I haven't tried that)
if the dismiss animation needs to do any non-standard things (mine luckily didn't), override animationControllerForDismissedController in the transition manager object and return a proper animator
if the target view controller is in a navigation hierarchy, then you also need to manually pop the navigation stack to the target controller before dismissing the presented screen (i.e. target.navigationController!.popToViewController(target, animated: false))
Complete code sample:
// custom navigation controller
override func segueForUnwindingToViewController(toViewController: UIViewController,
fromViewController: UIViewController,
identifier: String?) -> UIStoryboardSegue {
return CustomSegue(
identifier: identifier,
source: fromViewController,
destination: toViewController
)
}
// presented VC
var customTransitionManager: UIViewControllerTransitioningDelegate?
// custom segue
override func perform() {
let source = sourceViewController as! UIViewController
if let target = destinationViewController as? PresentedViewController {
let transitionManager = TransitionManager()
target.modalPresentationStyle = .Custom
target.customTransitionManager = transitionManager
target.transitioningDelegate = transitionManager
source.presentViewController(target, animated: true, completion: nil)
} else if let target = destinationViewController as? WelcomeViewController {
target.navigationController!.popToViewController(target, animated: false)
target.dismissViewControllerAnimated(true, completion: nil)
} else {
NSLog("Error: segue executed with unexpected view controllers")
}
}
I also met this problem when I need to pass data back from the modalpresented view.
I wandering around Google and here for a couple of hours but I couldn't find an answer that is easy to understand for me. But I did get some hint and here's a work around.
It seems that because it has to pass data back, and the dismissing process from the automatic Unwind is prior before the data passing which prevented the ViewController being dismissed. So I think that I have to manually dismiss it once one more time.
I got some luck here. I didn't notice that it was a child viewcontroller. I just configured it from the storyboard.
And then in the Unwind function, I added to lines to remove the child viewcontroller and the child view. I have no code in the sourceViewController.
Swift 4.1
#IBAction func unwindToVC(sender :UIStoryboardSegue){
if let source = sender.source as? CoreLocationVC{
if source.pinnedCity != nil{
clCity = source.pinnedCity
}
if source.pinnedCountry != nil {
clCountry = source.pinnedCountry
}
if source.pinnedTimeZone != nil {
clTimeZone = source.pinnedTimeZone
}
if source.pinnedLocation != nil {
clLocation = source.pinnedLocation
}
// I added 2 lines here and it just worked
source.view.removeFromSuperview()
source.removeFromParentViewController()
}
I am using a sidemenu for my app (Swift) and currently when I am taping on the menu I am creating a
new instance of a ViewController.
I have a UICollectionView with some data loaded, then I go to another ViewController from the menu and then when I get back on the previous ViewController the UICollectionView is empty and I have to reload the data.
destViewController = mainStoryboard.instantiateViewControllerWithIdentifier("SettingsViewController") as UIViewController
self.setViewControllers([destViewController], animated: true)
I need to find a way to pop to the View Controller at it's previous state. Any hints?
I have already used self.navigationController.popToViewController, same effect. I think the problem is that I am using instantiate method which create a new instance but I have no idea to check if there is already an instance and how to get back to it.
#IBAction func button_Back(sender: AnyObject)
{
self.navigationController?.popViewControllerAnimated(true)
}