Remove view controller from memory in swift - swift

I am using a few view controllers for my app. I switch view controllers programmatically by using this code:
func jumpToVC() {
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
self.performSegueWithIdentifier("whatVC", sender: self)
}
}
This works but the VC stays in the memory, it won't completely removes itself out of the memory. Adding this line of code did not help me out:
dismissViewControllerAnimated(false, completion: nil)
So how can I remove a view controller completely out of his memory?

Related

Get destination view controller when pressing back on navigation bar

When we perform a segue, it is easy to get the destination view controller so we can pass data to it using the prepare(for:) method.
I'd like to know the correct way to do this when the back button of a navigation controller is pressed.
I've managed to piece together something that works, but it feels wrong to be using my knowledge of the hierarchy of the view controllers within the navigation controller rather than getting the destination dynamically. Maybe i'm overthinking it?
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
// This method is called more than once - parent is only nil when back button is pressed
if (parent == nil) {
guard let destination = self.navigationController?.viewControllers.first as? MyTableViewController else {
return
}
print("destination is \(destination)")
// set delegate of my networking class to destination here
// call async method on networking class to retrieve updated data for my table view
}
}
As a general rule, trying to do that makes for too tight of a coupling. The VC you are navigating away from shouldn't need to know about where it's headed.
Probably better to implement your networking class methods in viewWillAppear or viewDidAppear inside your MyTableViewController class.
However, you could give this a try:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// make sure we're in a navigation controller
guard let navC = navigationController else { return }
if navC.viewControllers.firstIndex(of: self) != nil {
// we're still in the nav stack, so we've either
// pushed to another VC, or presented a full-screen VC
// (or maybe something else)
return
}
// we've been removed from the nav stack
// so user tapped Back button
print("back button tapped")
// see if we're navigating back to an instance of MyTableViewController
guard let destVC = navC.viewControllers.last as? MyTableViewController else {
return
}
// do something with destVC...
print("on our way to", destVC)
}
Note: I just did this as an example... it would need plenty of testing and possibly additional case handling.

How to remove duplicate VC in swift?

I am making a game, where I go from the main screen to a ArcadeViewController, which loads up the SKScene, and save the previous VC as prevVC.
I use segues created in storyboard to move between VC.
The problem is that each time I move to a VC, instead of moving into the old one, a copy gets created, and both of them start to run at the same time.
I tried removing them by running the following codes, when I move into the VC:
override func viewDidAppear(_ animated: Bool) {
UIApplication.shared.keyWindow?.rootViewController = self
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
prevVC.reloadViewFromnib()
prevVC.dismiss(animated: false, completion: nil)
UserDefaults.standard.set(0, forKey: "since_The_Last_Ad")
}
extension UIViewController {
func reloadViewFromnib() {
let parent = view.superview
view.removeFromSuperview()
view = nil
parent?.addSubview(view) // This line causes the view to be reloaded
}
}
It helped to reduce the number of copies created, but still the are some.
How can I remove duplicate views?
So the solution ended up being to use the normal segue to launch the ArcadeVC from GameVC(start VC) for the first time.
Then set the ArcadeVC as rootVC:
UIApplication.shared.keyWindow?.rootViewController = self
Then use a normal segue to go back to the GameVC.
After the ArcadeVC is set as the rootVC, just use normal segue to the ArcadeVC form GameVC, and unwind from GameVC to AracdeVC.

Present modal from NSWindowController without using Storyboards

I building a macOS framework that's running at the beginning of the login process and I want to show some information to the user in a window. I have an NSWindowController with a .XIB file and it's the main window for my framework.
I would like to add more presentAsSheet to another view. For that, I created an NSViewController with a .XIB to define the user interface that an I want to present in a modal but here I'm completely blocked because getting the contentViewController of the window its always nil
I'm initializing my main window like this:
func run() {
NSApp.activate(ignoringOtherApps: true)
mainWC = MainWC(windowNibName: "MainWC")
guard mainWC.window != nil else {
return
}
NSApp.runModal(for: mainWC.window!)
}
Where Im trying to present the other NSViewController"
self.window?.contentViewController?.presentAsSheet(customViewController)
In my MainWindowController I'm doing this:
#IBAction func btnAction(_ sender: Any) {
let customModal = CustomModal(windowNibName: "CustomModal")
self.window?.beginSheet(customModal.window!, completionHandler: { code in
print(message: "Clicked")
})
}
The modal never appears
You can't present the view until the window and the content view have been loaded
Either inside the window controller in windowDidLoad()
override func windowDidLoad() {
super.windowDidLoad()
// present the view
}
or even in viewDidLoad of the view controller
override func viewDidLoad() {
super.viewDidLoad()
// present the view
}

How to invoke a method from a modal view controller class in Swift?

Basically for this simple game app I have 2 different UIViewControllers called ViewController and PreviewController. PreviewController is opening view with the title screen and a label titled "Start game". When the label is tapped, it initiates a modal view controller (the ViewController class that has all the views for the actual game itself) and calls the "EnterNewGame" method from ViewController that sets up the game. Right now the issue I have is when calling this method, only part of the method seems to be running.
Here is the function in PreviewController that is being initiated upon tap:
#objc func handleButtonTap(_ recognizer: UITapGestureRecognizer) {
self.present(ViewController(), animated: true, completion: {() -> Void in
ViewController().enterNewGame()
})
}
And here is the EnterNewGame() method from ViewController
func enterNewGame() {
//show suit indicators when starting a new game
bluePlayerSuitsHidden = false
redPlayerSuitsHidden = false
game.blueTurn = true
self.setBackground()
self.cleanUpBoard()
self.createBoard()
self.displayBoard()
self.setSuitIndicators()
self.highlightCards()
playButton.isEnabled = false
}
Right now, when the label is tapped the screen transitions to the modal view controller but only displays a black screen with only one of the game setups (setting a few images on the top of the screen) working properly. I am sure that the EnterNewGame method works properly to actually start the game because I have tested it in isolation, so I think I am just not setting up the modal view controller properly or I have to call the method differently. Any help is appreciated, thanks.
Controller on which you're calling your method ins't the same instance as controller which you're presenting, you need constant (also your code can be simplified by avoiding using self references and writing name of completion parameter with specifing closure's parameter and return type)
#objc func handleButtonTap(_ recognizer: UITapGestureRecognizer) {
let controller = ViewController()
present(controller, animated: true) {
controller.enterNewGame()
}
}
Also, you can call this method on some other method inside your certain controller like viewDidLoad, viewWillAppear or you can create factory method which would return you certain set controller.
This last part leads me to idea: look how you instantiate your controller and look carefully if you don't need to instantiate it through storyboard or nib file.
class ViewController: UIViewController {
class func instantiate() -> ViewController {
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Identifier") as! ViewController
// let controller = ViewController(nibName: "ViewController", bundle: nil)
controller.enterNewGame()
return controller
}
}
Usage:
#objc func handleButtonTap(_ recognizer: UITapGestureRecognizer) {
present(ViewController.instantiate(), animated: true)
}

Automatically Reload TableViewController On Rewind

I am working on an app where it starts out at a tableViewController which loads and displays data stored in a Realm database. I have it so I can create a new entry in my Realm database in a separate scene and the save button unwind segues back to the initial tableView.
I current have it so the tableViewController will reload the tableView on pull down (something I Learned here, second answer down) but I would be better if the tableView would reload its self automatically upon unwind, displaying all the data in my database, including my new entry. Could someone please direct me to a tutorial that will teach me how this is done.
Additional info: My app is embedded in a navigation controller. The save button is located the bottom tool bar.
You can use NSNotification for that.
First of all in your tableViewController add this in your viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshTable:", name: "refresh", object: nil)
}
And this will call one method from your class:
func refreshTable(notification: NSNotification) {
println("Received Notification")
tableView.reloadData() //reload your tableview here
}
So add this method too.
Now in your next view controller where you add new data into data base add this in you unWindSegue function:
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil, userInfo: nil)
Hope it will help
Try reloading your tabledata in viewWillAppear in initial (tableview)controller.
override func viewWillAppear(animated: Bool) {
self.tableView.reloadData()
}
Or call again the function through which you are loading your data from Realm like
override func viewWillAppear(animated: Bool) {
getMyData() //or whatever your function name is
}
If you are using storyboard unwind segue, try using
func unwindSegue(segue:UIStoryboardSegue) {
if segue.identifier == "identifier" {
getData()
self.tableView.reloaddata()
}
}