Remove current ViewController after instantiate and presented the next VC - ios

In iOS project, XCode, I have two VCs, VC1 and VC2. What I want to do is in VC1, have a button to go to VC2 using
let VC2 = self.storyboard?.instantiateViewControllerWithIdentifier("VC2") as! VC2
self.presentViewController(loginVC, animated: true)
After VC2 appears, I will never need to go back to VC1 so I want to remove it completely as VC1 has some downloading function that may still run in background. So I want to remove VC1. I have tried the following:
let VC2 = self.storyboard?.instantiateViewControllerWithIdentifier("VC2") as! VC2
self.presentViewController(loginVC, animated: true) {
self.removeFromParentViewController()
}
However, that does not seem to do the job as I still see the background download in progress. Is there a simple way of doing it? I do not really want to include navigation controller in this case.

Related

Segue Opposite Push Effect

VC1 = Table View
VC2 = New VC
So I'm using A segue to show VC2 from VC1. When using the segue, VC2 shows coming from the bottom of the screen and covering up VC1. When I go back from VC2 to VC1, VC1 does the same animation.
What I was wondering is, is there any way to reverse the back segue from VC2 to VC1 so either VC2 looks as though it is going downwards or VC1 is coming over it from the top of the screen.
Thanks,
Riley
This push effect animation does present modally segue with destination ViewController's transition style Cover Vertical
So when you want to go back from VC 2 to VC 1 with animation opposite to push effect, you can do it just by dismissing VC 2. You don't have to use any segue, just dismiss ViewController.
So from VC 1 you can go to VC 2 by performing segue (you've already done that)
performSegue(withIdentifier: "identifier", sender: self)
and then from VC 2 you can go back to VC 1 by dismissing VC 2
dismiss(animated: true, completion: nil)

Push view controller, but go back to another one

Let's say that on my regular flow I have
VC1 -> VC2 -> VC3 -> VC4
But I have a requirement that when I touch a special button on VC1 it needs to go to VC4.
No problem there. The problem is that when I tap the back button on VC4, it needs to go back to VC3 instead of VC1.
I already tried pushing From VC1 -> VC2 and VC2 -> VC3 without animation and then VC3 -> VC4 with animation, but you can see a quick glimpse of VC3 which looks awful.
Any ideas?
I think in this specific case, you can insert a view controller on the navigation controllers stack after presenting the 4th viewController
if let navigationController = navigationController {
navigationController.pushViewController(vc4, animated: true)
let vc3Index = navigationController.viewControllers.count - 1
navigationController.viewControllers.insert(vc3, atIndex: vc3Index)
}
This should place VC3 next in line when the user presses back from VC4. Untested code, btw.
You can modify the UINavCon's viewControllers to achieve the order you want.
func specialButtonInVc1() {
self.navigationController?.pushViewController(fourth, animated: true)
self.navigationController?.viewControllers = [self, second, third,fourth]
}

Navigation Controller Loop

I'm making an app which has a shopping cart feature. The shopping cart VC is accessible from several VCs via a button on the navigation bar. So far I only have one Navigation Controller and as shown in the image, each VC takes me to the next one, nothing fancy. The 3rd VC (blue arrow pointing to) is a VC that shows the product/item's details and enables me to add it to the cart.
Problem comes with the Shopping Cart VC. To edit the item, I'd like to re-use the same product/item's details (blue arrow pointing to) VC I used earlier.
Right now, I don't really have an issue but I have noticed that once I created the blue segue, the Navigation Bar of the 3rd VC disappeared in the Storyboard however I was still able to see it when I ran the app.
NOTE:
All the segues in the picture are "Show"
The shopping cart VC doesn't have a button to show itself like the other 3 does. So technically I prevented endless/loop of the shopping cart VC - product/item details VC showing one another.
My questions are:
Is it wrong to design the app that way with a VC going to another VC and that other VC can go back to the first VC? Am I going to face issues down the road, perhaps memory leaks of some sort?
Is there a better way to achieve what I am trying to achieve?
If someone needs further explanation please let me know and I'll edit my question.
EDIT: To clarify, the blue segue is basically a button in a UITableView in the cart VC. If you tap that button, it should open the product/item details VC and lets you edit the item's color, etc. In the item details VC, instead of adding the item as a new item to the cart, I'd show an Edit button which would send the edit request to the API and dismiss the VC back to the shopping cart, or I could use the back button in the navigation controller to get back to the shopping cart.
EDIT2: #beshio
Thanks for the answer. However VC1 is actually my root VC where all the app starts. I didn't get why removed VCs from the Navigation Controller's stack. I would like the Back button to work as intended.
So far I have achieved what I wanted but I'm afraid that having two VCs segue-ing to each other would cause a problem. I have already disabled the Cart button in VC3 in case VC3 was presented from the Cart so loops would be prevented. I am only worried about any memory leaks down the road.
It's possible to achieve this kinda transition.
Here, I describe how to implement this with your chart.
As your chart shows, assume you have VC1, VC2, VC3 (top to bottom) and VCX (w/ blue box). You need to define transitions and associated animation directions (push: right-to-left or pop:left-to-right). As your chart, if you define the transitions and animations as:
VC1 to : VC2(push), VCX(push)
VC2 to : VC3(push), VCX(push)
VC3 to : VCX(push)
VCX to : VC3(pop)
and assume we have all of the view controllers instantiated already, then,
VC1 to VC2 transition
at VC1:
navigationController!.pushViewController(VC2, animated: true)
at VC2:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// remove VC1 from navigation stack (I assume VC1 is not a root VC)
let allControllers = NSMutableArray(array: navigationController!.viewControllers)
allControllers.removeObject(at: allControllers.count - 2)
navigationController!.setViewControllers(allControllers as [AnyObject] as! [UIViewController], animated: false)
}
VC1 to VCX transition
at VC1:
navigationController!.pushViewController(VCX, animated: true)
at VCX: (A)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let allControllers = NSMutableArray(array: navigationController!.viewControllers)
if (navigationController!.viewControllers[allControllers.count-2] != VC3) {
// if not from VC3, remove VC from stack and put VC3
allControllers.removeObject(at: allControllers.count - 2)
allControllers.insert(VC3, at: allControllers.count - 1)
navigationController!.setViewControllers(allControllers as [AnyObject] as! [UIViewController], animated: false)
}
VC2 to VC3 transition
at VC2:
navigationController!.pushViewController(VC3, animated: true)
at VC3: (B)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let allControllers = NSMutableArray(array: navigationController!.viewControllers)
if (navigationController!.viewControllers[allControllers.count-2] == VC2) {
// if from VC2, remove it
allControllers.removeObject(at: allControllers.count - 2)
navigationController!.setViewControllers(allControllers as [AnyObject] as! [UIViewController], animated: false)
}
}
VC2 to VCX transition
at VC2:
navigationController!.pushViewController(VCX, animated: true)
at VCX: same as (A)
VCX to VC3 transition
at VCX:
navigationController!.popViewController(animated: true)
at VC3: same as (B)
Note viewDidAppear is called when users swipe (left-to-right) to go back and cancel it on the way (== swipe back to left). So, you need some more small code at viewDidAppear against that situation.
If you want the different animation direction, by manipulating stack and using push/pop, you can easily achieve it. This explains how.
Awesome.
In these scenarios you should use setViewControllers([UIViewController], animated: Bool) to get the desired viewcontrollers in the stack when you get many controllers stacked up in cycles.
Other way is you write your own class derived from UINavigationController having methods, pushToCheckout(animated:Bool), popToEditCart(animated:Bool) , removeIntermediateControllers()

Attempt to present VC2 on VC1 which is already presenting

Presently at a lost for why I am receiving the message of Attempt to present VC2 on VC1 which is already presenting when the VC1 does both the presenting and dismissing of the VC2. There are a few other questions and a lot of examples and I've not been able to resolve it from looking through them.
VC2 is called RoadwaysViewController and is opened as a modal. The only catch I see is that VC2 is used by 2 other VC. Things work great for awhile and then the problem arises when I've moved back and forth from VC to VC after awhile and in each I've used VC2. VC2 was intended to be used in multiple places since the data is the same for all.
Presenting and dismissing are both done from VC1.
class level variable
var roadwaysViewController = RoadwaysViewController()
Here is how it is presented
func roadwayTapped(){
roadwaysViewController = storyboard?.instantiateViewController(withIdentifier: "RoadwayModal") as! RoadwaysViewController
roadwaysViewController.delegate = self
self.present(self.roadwaysViewController, animated: true)
}
Here is how it is dismissed on VC1 the callback from the delegate.
func sendValue(value: Int, name: String) {
roadwaysViewController.dismiss(animated: true, completion: {
if value == 999 {
return
}
self.groupId = value
self.roadName.text = name
self.setupTableData()
})
}
I've tried checking to see if VC2 is presenting and then did dismiss. Tried this in the function that does the presenting, viewWillAppear and viewDidAppear.
I understand you can't have 2 VC presented at the same time but why it isn't getting properly dismissed I'm struggling to understand why. I don't believe I want to dismiss the parent and then show the child since the child is a modal.
Any help, ideas or suggestions is appreciated.

Pass data to VC that is not the previous VC

When I go from VC1 to VC2, if VC2 gets dismissed, I could easily pass data back to VC1 by setting protocal in VC2 and have VC1 conform to it. I want to do something similar, however, with the following difference
When I go from VC1-> NavVC->VC2-> VC3. When VC3 gets dismissed, VC1 is shown. I want to be able to pass data back to VC1 and initiate some function in VC1. For example, I have an image to upload in VC3. As soon as VC3 gets dismissed, I am hoping to have a function in VC1 such as the following function where the image was the data from VC3
func uploadInitiate(image: UIImage) {}
Relationship of the three VC
VC1 is normal VC. It presents VC2 via
let navController = UINavigationController.init(rootViewController: VC2)
self.navigationController?.presentViewController(navController, animated: true, completion: nil)
VC2 is a custom FusumaCamera photo picker from cocoapods.
Once image is selected, I go to VC3 with the selected Image via
self.navigationController!.pushViewController(postVC, animated: true)
At VC3, I allow the user to interact with the image and make comments and press a button to upload to the server. I thought it would be nice to dismiss the VC straight away after button press and allow VC1 to initiate the upload with the data the came from VC3 (That way I can have a progress bar under navigation bar or display any warnings there)
You have multiple patterns that you can apply in similar situations. i will go with the delegation (recomended) example. Imagine you have VC1->
that presents modally a UINavigationController as a root controller and from there VC2 pushes VC3 to the stack. You have to define multiple protocols that will be called on the way you dismiss the VC3
protocol DismissVCDelegate : class {
func dismissViewAndStartUpload(shouldUpload: Bool)
}
Then store the property:
weak var delegate: DismissVCDelegate!
Set the delegate when to the VC3 when you push it from VC2 and conform the the protocol you defined in VC2.
Apply the same pattern all the way back to VC1 where you have passed multiple times the protocol back and you can start you upload task and you should dismiss the modally presented navigation controller like that:
func dismissViewAndStartUpload(shouldUpload: Bool) {
self.presentedViewController.dismissViewControllerAnimated(true, completion: nil)}

Resources