Detecting that back was pressed to get to current view controller - ios

I am using the navigation controller to go back from one view to previous view using the code below.
ChildViewController.swift:
self.navigationController.popViewControllerAnimated(true)
I need a way to detect that the navigation controller went to the previous view in the actual previous view like below.
ParentViewController.swift:
func backWasPressed(viewControllerIdentifier: String!) {
// if back was pressed from this view controller and not from any other view
if viewControllerIdentifier == "ChildViewController" {
// do stuff here
}
}
Is there anyway to do this?

Take a look at UINavigationControllerDelegate

You don't need to know this. You may think you do, but you don't. This entire proposed architecture is specious:
func backWasPressed(viewControllerIdentifier: String!) {
// if back was pressed from this view controller and not from any other view
if viewControllerIdentifier == "ChildViewController" {
// do stuff here
}
}
If a pushed view controller has some info to communicate to a view controller further down the stack, that is the job of the pushed view controller when it is popped. It knows it is being popped, and it knows how to access the other view controller (and you can use a delegate architecture if there's any doubt about that), so the problem is properly solved in that way. It's exactly the same as when a presented view controller needs to communicate back to its presenter at dismissal time.

Related

Is it fine to nest a UIViewController within another without using addChildViewController?

I'm trying to make a custom ContainerViewController, but due to lots of difficulties with the ViewController transitions and making everything interactive, I've decided to mimic that functionality myself.
What I basically want to do, is have a paginated UIScrollView (the HeaderView) on the top control different another UIScrollView (the ControllersView) below that contains ViewControllers as pages so that as you swipe to a new page on the HeaderView, it also swipes to the next viewcontroller on the ControllersView. This is what the setup would look like.
My question is, is there anything wrong with having the aforementioned setup? All I'll do to add the view controllers to the ControllersView is just something like: controllersView.addSubview(pagecontroller1.view).
Some posts online seem to say that "the appropriate ViewController functions won't be called" or whatever. What do I seem to be missing here? I'm guessing there's a lot of dismissing and admitting of ViewControllers that I need to call every time a ViewController is out of frame right?
To clarify the question: Is it ok/efficient to do this? Should I be calling some viewWillAppear/disapper functions when the VC's get in and out of frame? If so, what should I call? I'm realizing that if I were to set things up this way, I need to manage a lot of things that are usually handled automatically, but as I mentioned before, custom ContainerViewControllers have failed me and I'm going with this.
PS. If you seem to still be lost on how this will look like, see my previous question here where I originally wanted to use a Container ViewController. There's a much better mockup there.
You can add and remove VC In Container Views
For - Is it ok/efficient to do this? Should I be calling some viewWillAppear/disapper functions when the VC's get in and out of frame? If so, what should I call?
As, We need to call WillAppear and Disappear Func when Adding and removing a VC , Thus Try using below Functions That will Handle these Responses
I use the Two specific Functions to add and remove Controller in ContainerView/UIView/SubView in ScrollView inside a UIView
To Add
private func add(asChildViewController viewController: UIViewController)
{
// Configure Child View
viewController.view.frame = CGRect(x: 0, y: 0, width: self.firstContainer.frame.size.width, height: self.firstContainer.frame.size.height)
// Add Child View Controller
addChildViewController(viewController)
viewController.view.translatesAutoresizingMaskIntoConstraints = true
// Add Child View as Subview
firstContainer.addSubview(viewController.view)
// Notify Child View Controller
viewController.didMove(toParentViewController: self)
}
To Remove
private func remove(asChildViewController viewController: UIViewController)
{
// Notify Child View Controller
viewController.willMove(toParentViewController: nil)
secondContainer.willRemoveSubview(viewController.view)
// Remove Child View From Superview
viewController.view.removeFromSuperview()
// Notify Child View Controller
viewController.removeFromParentViewController()
}
Creating Object
private lazy var FirstObject: firstVC =
{
// Instantiate View Controller
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "firstVC") as! firstVC
// Add View Controller as Child View Controller
self.addChildViewController(viewController)
return viewController
}()
For - controllersView.addSubview(pagecontroller1.view)
Answer - Yes Approbate func wont be called if pagecontroller1 is not loaded in to memory stack, to load that you need to notify pagecontroller1 that it is going to be added to memory stack as Child View , Just as We initiate a Controller and basically notifies the Controller to get its component loaded to memory stack to get some memory allocations
For Question - Is it fine to nest a UIViewController within another without using addChildViewController?
Check apple Documentation - https://developer.apple.com/documentation/uikit/uiviewcontroller/1621394-addchildviewcontroller
This is necessary just as to notify the controller who is going to be added in Another Parent View as Child
Sample Project
https://github.com/RockinGarg/Container_Views.git
Or
https://github.com/RockinGarg/ContainerView-TabBar.git
If Question is Still not answered Please Tell me what Func Exactly you want to handle by yourself

UINavigationController popToRootViewController method cannot be called by a delegate?

I have a UINavigationController which works great. Each view controller has its own button that pops the stack back to its root which also works great. However, I'd like to also be able to pop the stack back to its root by pressing a button on the tab bar (which is obviously in an entirely different class outside of the navigation stack).
Therefore, I created a delegate in the tab bar class which finds the view controller at the top of the stack and calls the method in that view controller to pop the stack back to the root. I printed something to the console to verify that the delegate is set up correctly and it is. Everything works exactly as it should, except that pressing the tab bar doesn't pop the stack back to its root.
Thoughts?
This is the view controller at the top of a UINavigationController stack
class BlankViewController202: UIViewController, MainContainerViewControllerDelegate {
// pop to root
func popToRoot() {
self.navigationController?.popToRootViewController(animated: true)
print("success")
}
}
When this function above is called from within the view controller (when the user presses the button on the view controller itself), it pops the stack. But when this same exact method is called by a delegate from the tab bar, it doesn't pop the stack (but it does print to console so I know its hooked up properly).
This is where the button resides in the tab bar that when pressed should pop the stack back to its root
protocol MainContainerViewControllerDelegate {
func popToRoot()
}
class MainContainerViewController: UIViewController {
func moveToTab3(sender: UIButton!) {
// ...
let banana = BlankViewController202()
self.delegate = banana
delegate?.popToRoot()
}
}
The problem is that BlankViewController202() makes a whole new, separate BlankViewController202 — it is not the particular BlankViewController202 that is already in the interface as part of the navigation controller interface. It is that BlankViewController202 you want to talk to.
I think, you error delegate pattern. You can see again model delegate use protocol. If you use protocol, you delete line code "let banana = BlankViewController202()".
Just Follow some steps
1) Make one Object of UINavigationViewController in AppDelegate and you can access it with shared object of app delegate.
2) The first line of moveToTab3 will be [Appdelegate sharedObject].navigationViewControllerVariable = self.navigationViewController
3) In Your delegate method write this line
[[Appdelegate sharedObject].navigationViewControllerVariable popToRootViewController:true]
this will work definitely :)

ViewController popped immediately after been pushed in UINavigationController

I bounce in a quite weird issue. When I push a specific view controller for another one, the former get dismissed soon after being showed. When I push it fom the main View Controller, it stays put without any problems. I put breakpoints and the viewDidDisappear is in fact called just after the viewDidAppear.
By smell it look like the second view controller becomes nil in one way, but how is it possible if that is wired to the storyboard?
Has anyone got any idea about what could be the reason for the weird behavior?
The main view controller and the first view controller are both in Swift, the pushed controller is still in Objective-c.
This is how I open the second view controller:
func didSelectRow(indexPath: NSIndexPath, from owner: DestinationsViewController){
if let currentElement=DestinationsContentProvider.sharedContentProvider().stations[indexPath.row]{
print("a \(indexPath.row) elemento \(currentElement)")
let targetModel = currentElement.model
//NSLog(#"targetMetro:%# targetPaletta=%#", owner.targetMetro, owner.targetPaletta);
if ((targetModel != nil) && (targetModel!.myTraffic != nil)){
targetModel!.segueExecute()
}
}
segueExecute is called on the model that is not dismissed. I put a breakpoint on the dealloc and it is never reached.
The only peculiar issue is that in the model I perform the segue on the main controller instead of the actual controller by this piece of code:
mapController.performSegueWithIdentifier("ShowWaiting", sender:self)
Still the same behavior happens even if I manually push the controller by executing:
let mainStoryboard:UIStoryboard!
if (UIDevice.currentDevice().userInterfaceIdiom == .Pad){
mainStoryboard=UIStoryboard(name:"StoryboardiPad", bundle: nil)
} else {
mainStoryboard=UIStoryboard(name:"MainStoryboard_iPhone", bundle: nil)
}
let controller = mainStoryboard.instantiateViewControllerWithIdentifier("Situation") as! StationSituation
controller.model=targetModel;
InArrivoHDViewController.sharedDetailController().navigationController?.pushViewController(controller, animated: true)
without using the segue construct.
Just check whether second view controller used for pushing is a property or not. If secondVC instance is created within the method in which pushing is done, secondVC will become nil after execution of the method.
I fixed the issue by directly calling performSegue on the view controller rather than delegating it to the root controller. For some reason this delegation works if there is the same kind of view controller on the Navigation queue in which you are pushing the controller: I have this construct in another class and I just checked it actually work. Otherwise the effect is the weird one I experienced.
I think, but I may not be sure, that in Objective-c the situation was different.

Dismiss Current View controller and open new view controller - ios Swift

I have three view controllers. Now I am in second view controller. I want to dismiss my current view controller and open third view controller. How can I do it? I can dismiss third view controller as well as second view controller by using the following code.
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(false, completion: nil)
My problem here is when I use this function screen flickering. That means the second view controller showing and dismissed. So how can I do it?
I want to dissmiss the currentview cotroller and open new view
controller
It is possible in Android. So I hope it is possible in ios too. How can I achieve this. Please some one help me.
Edit 1:
I try to dissmiss the current view controller and open new view controller using the following code. Then my second and third both are closed.
self.dismissViewControllerAnimated(false) {
// go back to MainMenuView as the eyes of the user
presentingViewController.dismissViewControllerAnimated(false, completion: nil)
}
I found the solution
I change all the view controller to the child of navigation controller. And remove the history from the third view controller like the following
let tempVCA = self.navigationController!.viewControllers
for tempVC: UIViewController in tempVCA {
if (tempVC is SECONDVC) {
tempVC.removeFromParentViewController()
}
}
I am using this piece of code from the Third view controller's viewdidload func

Unwind segue from navigation back button in Swift

I have a settings screen, in that I have a table cell. By clicking on that I take to another screen where user can choose an option and I want it back in the previous view controller when back button is pressed.
I can put a custom bar button item, but I want to return to the parent view controller using the back button in the navigation bar rather than with a custom button on the view.
I don't seem to be able to override the navigation back button to point it down to my unwind segue action and since the back button doesn't appear on the storyboard, I cant drag the green Exit button to it
Is it possible to unwind a push segue with the back button?
Here's my solution, based on Objective-C code from Blankarsch to this StackOverflow question: How to trap the back button event
Put this code inside the View Controller you want to trap the Back button call from:
override func didMoveToParentViewController(parent: UIViewController?) {
if (!(parent?.isEqual(self.parentViewController) ?? false)) {
println("Back Button Pressed!")
}
}
Inside of the if block, handle whatever you need to pass back. You'll also need to have a reference back to calling view controller as at this point most likely both parent and self.parentViewController are nil, so you can't navigate the View Controller tree.
Also, you might be able to get away with simply checking parent for nil as I haven't found a case where pressing the back button didn't result in parent being nil. So something like this is a bit more concise:
override func didMoveToParentViewController(parent: UIViewController?) {
if (parent == nil) {
println("Back Button Pressed!")
}
}
But I can't guarantee that will work every time.
Do the following in the view controller that has the back button
Swift 3
override func didMove(toParentViewController parent: UIViewController?) {
if !(parent?.isEqual(self.parent) ?? false) {
print("Parent view loaded")
}
super.didMove(toParentViewController: parent)
}
I tried the same and it seems that you cannot catch the standard back button's action so the solution will be to use a custom button and bind it to a segue which leads back to the previous page.
You could use some sort of delegation as you did or use a custom back button and an unwind segue.
Better even, you could handle passing data between your view controllers using a mediator:
http://cocoapatterns.com/passing-data-between-view-controllers/

Resources