Pass data to VC that is not the previous VC - ios

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)}

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)

How do I delegate to view controller more than 1 view controller up the stack?

Scenario: I have a view controller (vc1) that presents another view controller (vc2). And then vc2 presents vc3. How can I set vc1 as a delegate for vc3?
Situation: While in vc3, I intend to pop back to vc1 and have it execute some code. But, since the vc3 instance is created in vc2, vc1 has no direct link to vc3 from vc1.
Here is a simplified picture of what I'm trying to achieve:
vc1
let vc2Instance = vc2()
navigationController?.pushViewController(vc2Instance)
class vc1: UIViewController, tellVC1ToDoSomethingDelegate {
func vc1DoSomething(withThis: String) {
// for instance: present another VC not currently in stack
let randomVC = RandomVC()
}
}
vc2
let vc3Instance = vc3()
navigationController?.pushViewController(vc3Instance)
vc3
protocol tellVc1ToDoSomethingDelegate() {
func vc1DoSomething(withThis: String)
}
class vc3: UIViewController {
weak var vc1Delegate: tellVC1ToDoSomethingDelegate?
func pushRandomVCWithString(myString: String) {
// code to dismiss view controllers up to vc1 in stack
if let vcStack = self.navigationController?.viewControllers{
self.navigationController?.popToViewController(vcStack[vcStack.count - 2], animated: true)
vc1Delegate.vc1DoSomething(withThis: myString)
}
}
Here's my issue:
If I was marking vc1 as a delegate for vc2 (Just 1 VC up the stack), I'll simply type
let vc2Instance = vc2()
vc2Instance.vc1Delegate = self
How do I access self (of vc1) when no instance of vc3 exists in vc1? Is chaining delegates from vc3 to vc2 then to vc1 the only way out? I imagine this will be ugly when there are several vc's in-between.
There are two way to do this, one is delegation and another one is Notification.
Delegation:
As you asked, you are trying this one. Just:
create a delegate variable in vc2 and vc3 both.
while creating the object of vc2 in vc1 pass the self in the delegate variable of vc2.
while creating the object of vc3 in vc2 pass the self.delegate in the delegate variable of vc3.
Now as you call the method in vc3 like self.delegate?.anyMethod() will get the call in both vc2 and vc3.
Notification:
As of the notification you can broadcast a notification of custom type with some info for vc3. and can add observer to observe the notification in either in vc1 or any other vc, you will receive the call with passed data.
Learn more about Notification here
As my personal suggestion first one is better in your case.
In many ways you can achieve this. Some of them listed below.
If VC1 is your root VC for the navigation stack you can use the
below API in VC3
popToRootViewController(animated:)
Other way you can get the viewControllers array property from the
navigationController and get the VC1 from the array and use the
below API in VC3
popToViewController(_:animated:)
Hope this help. Please comment any doubt.

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]
}

Remove current ViewController after instantiate and presented the next VC

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.

How to properly dismiss the presented ViewController and go back to the parent ViewController?

I'm facing an issue that I will describe and have found some similar questions, but I don't believe pertains to my issue.
I have a UITabBarController with 2 tabs for VC1 and VC2. VC1 segues to VC4. VC2 segues to VC3 and VC3 segues to VC4. VC4 contains an MPMoviePlayerViewController instance, as depicted below:
- - - - - -> VC1 \
TAB BAR VC / - - -> VC4
\ /
-> VC2 -> VC3 -> /
I have a Notification that listens to when the Video finishes, and dismiss VC4, and go back to whichever parent VC presented VC4, i.e. if VC1 presented VC4, then upon dismissal of VC4, should go back to VC1. Likewise, if VC3 presented VC4, then upon dismissal of VC4, should go back to VC3.
In VC4:
override func viewDidLoad()
{
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "moviePlayerPlayBackDidFinish:",
name: MPMoviePlayerPlaybackDidFinishNotification,
object: player?.moviePlayer)
}
func moviePlayerPlayBackDidFinish(notification: NSNotification)
{
NSNotificationCenter.defaultCenter().removeObserver(
self,
name: MPMoviePlayerPlaybackDidFinishNotification,
object: notification.object
)
player!.view.removeFromSuperview()
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
I don't think I'm using the code properly because self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil) stays at VC4.
I found some similar questions:
How to dismiss the current ViewController and go to another View in Swift
How to dismiss VIewController iOS Swift
modal View controllers - how to display and dismiss
However, the sequence segues are different, therefore I can't follow the suggestions.
How can I achieve this? Thanks
UPDATE:
The following code in moviePlayerPlayBackDidFinish properly dismisses VC4 and back to parent VC1 if going from VC1 -> VC4:
self.dismissViewControllerAnimated(true, completion: nil);
However, VC4 does not dismiss when going from VC3 -> VC4.
Finally solved my issue by adding the additional code in moviePlayerPlayBackDidFinish:
self.navigationController?.popViewControllerAnimated(true)
After much research, I found the solution from here: dismissViewControllerAnimated() does not dismiss view controller

Resources