Understanding performSegueWithIdentifier - ios

Can someone more knowledgeable than I explain performSegueWithIdentifier:sender: for me? I need to switch views (and classes) and also carry a few NSStrings and IDs over to that view's class. I was wondering if this is possible with performSegueWithIdentifier:sender:
Thanks!

First, you have to have set up the segue in your storyboard and give it the appropriate identifier. (Click on the segue (left panel) and then click on Attributes (right panel).
You can then link this to buttons or selection of table rows from your storyboard, or you can call it in code using performSegueWithIdentifier:sender:.
After this, your view controller will be sent the prepareForSegue:sender: message. You override this method in your view controller subclass, and can configure the target view controller as follows:
TargetViewController *targetVC = (TargetViewController*)segue.destinationViewController;
targetVC.string1 = string1;
And so forth. The sender in this method will be the object that you use as the sender in the original method call.

Most segues are initiated automatically as the result of some user interaction. For instance, if you have a segue that is wired up from a button to a scene in a storyboard, when the button is tapped the segue will automatically initiate.
Occasionally, it makes sense to trigger a segue programmatically - e.g. you have a High Scores scene that is displayed when the user wins a round of a game. There's no way to express the concept of winning in the storyboard itself, so you can instead create a segue, assign an identifier to it, and invoke -performSegueWithIdentifier:sender: at runtime.
The other segue related method on UIViewController, -prepareForSegue:sender:, is the method you should override to perform any customization on the destination view controller.

In prepareForSegue:sender: you get a chance to configure the destinationViewController: that's where you'd pass it the data it needs. It's discussed in Cocoa Application Competencies for iOS.

Today I ran into the issue of performSegueWithIdentifier: not executing due to the fact of not having set a delegate queue on my URL session.
So by any chance, check if you are actually setting a delegate queue when creating your URLSession, else URLSession will create it's own.
urlSession = [NSURLSession sessionWithConfiguration:sessionConfigObject
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
I mention this here because I quite often see URLSession handling ending up calling some sort of UI related activity. And performSegue needs to be executed on main, or else it will do just nothing.

Related

Should I use didSelectRowAtIndexPath, prepareForSegue, or both?

I am building an app that presents UITableView to the user, from which the user needs to make a selection. Once the selects a row, they are presented another ViewController which displays details of the selection they made on the previous ViewController.
Here's the catch: After the user makes the selection, the app needs to make a call to the network to retrieve some data to be displayed on the next ViewController. I was planning on calling a method from the prepareForSegue method which would return the results from the network call, and then call the appropriate ViewController, but I'm wondering if this is something that should be called from the "didSelectRowAtIndexPath" method (which I have not implemented).
My fear is that the second ViewController will be called BEFORE the call to the network returns with the data that I need to display. Is this even the place to put such a network call, or should I make this call from the "viewDidLoad" method of the destination ViewController instead? What is the best architecture and why?
Call prepareForSegue method inside didSelectRowAtIndexPath method to navigate to secondViewController then to display details, make network call inside viewDidLoad method of secondViewController and show activityIndicatorView until the data is fetched.
I would use some asynchronous loading starting in prepareForSegue and managed in the details view. Once the details view appears, you have to inform user that something is loading (an appropriate turning wheel exists for it), and then populate the interface after the loading has been done (or manage loading error).
But using viewDidLoad of the details view would be ok too, provided you always inform user of the current loading...
Don't use didSelectRow because it is just intended for selection... There can be no navigation on a tableview cell!
Asynchronous is preferred because it does not block user in some weird state waiting something he does really know.

iOS object or delegate between two controllers?

Evening, my question is full about theory.
I understood reading from Apple developer documentation that is better to use the Delegates Pattern to keep track of some object attributes. In this way we can access the delegate without access to the object. (I really didn't get the reason of this choice)
I also understood that is better to define: protocolDelegate: class
and when we are declaring the delegate inside the class it's better to use the weak word to prevent some "kind of problem cycle". (??)
So, while I was playing a bit with code, I've discovered that you can't pass a weak delegate between two view controllers, because of course, when you change the controller, the weak delegate is going to be deleted because is a weak thing (or at least this is what I understood).
So, I have to choose between 2 options:
make the delegate "strong" deleting the weak key.
or pass the object in the segue and keep the delegate as weak.
I have a lot of confusion, can you clear my mind? :D
The cycle you're referring to is called a retain cycle.
Let's use a concrete example to clear this up: say you've got a UIViewController which has a UITableView. The view controller has a strong reference to the table view. The view controller now wants to act as the delegate to the table view.
Now, if the table view would have a strong reference to its delegate, we would have the following situation: the view controller has a strong reference to the table view, and the table view in turn would have a strong reference back to the view controller. Thus neither can ever get deallocated.
To break this cycle, references to delegates are usually weak. This allows the retain count of the view controller to drop to 0 eventually, which can in turn release the table view.
Your classes that want to use delegates should also follow this pattern and use weak references to their delegates. You should thus pass the required references via your segue.
I will concentrate on the first part of your question, since the previous answers have covered the rest pretty well.
Consider the following situation: you have a class that handles some kind of network connection - it sends a request to a server and gets a response. Outside of this class there is a viewController that has a button that triggers the request and a view which presents the response to the user.
Basically, the network handling class should be able to get some message from the viewController (button pressed) on one hand and pass the viewController the response on the other. So there should be bidirectional communication between the two classes. While the passing of the buttonPressed message to the network handling class is pretty obvious, the reverse part (passing the response) is a bit more tricky, because the network handling class should not be aware of who created it and who calls it (good OO practices and memory leaks prevention).
That's the point where the delegate pattern comes in. It allows an object to pass data to whoever is interested in it without knowing anything about the recipient. The class that passes the response only knows some 'delegate' and not another class. In addition you can take out the network handling class as is and put it in another project. Because it isn't supposed to know any other class from its original project, only some 'delegate', it can be put into another project without any modifications.
I hope it can help you to get the reason of the choice.
I think pass the object with segue, Segues are a very important part of using Storyboards in Xcode. We can go over the different types of seguesanother time, but this one shows you how to use the “Show” segue, as well as how to pass data between the two view controllers to customize the second one with whatever custom data is required.
You can easily use segues example; Under below you can send currentstring to destinationViewController inside sentstring , also ShowSegue is your segue identifier
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowSegue" {
if let destinationVC = segue.destinationViewController as? OtherViewController {
destinationVC.sentstring = currentstring
}
}
}
Navigation between viewcontrollers maintain stack of viewcontrollers.
For example aVC is firstviewcontroller then top of stack will be aVC,
now when you push or show another viewcontroller say bVC then now top of statck is bVC. So stack looks like,
aVC -> bVC(top)
now you push another cVC then,
aVC -> bVC -> cVC(top).
So top of stack is always visible to user.
at current situation, aVC and bVC and cVC are not deallocate. they are in memory. But if you pop or dismiss cVC, then it will deallocate from memory and now your top of stack looka like,
aVC -> bVC(top).
So viewcontrollers live in stack till they are not popped or removed. So, they are strog reference by default.
Segue is nothing but you can say that they are graphical representation of push or pop operation.
another thing is that delegate should be weak that because it can create retain cycle if they are strong.
you can called delegate as representative in general sense.
Now, if you are using segue, send your object in prepareForsegue and it will manage everything else.

Button changes label.text; no prepareForSegue

Long story short. Massive app that uses a container and the notorious "empty segue". The container takes NSNotificationCenter notifications to perform segues off of a subclassed NSObject that contains the segues. This means the method "prepareForSegue" is not being used and if used, does nothing. NSNotification won't work on making the label.text value change if the view controller hasn't been placed on the stack yet, therefore I need something that will go first round. Here's the story:
View controller 1 has 2 buttons that when clicked will take you to view controller 2. View Controller 2 has a label that will change text depending upon which button you select. PrepareForSegue method cannot be used due to implementation of NSNotificationCenter and a container that is the listener.
I've tried using NSNotificationCenter, but as stated before, only works once the view controller has made it to the stack. I need the change to happen the first time. Thanks.
I have an IBAction tied to a button and that IBAction contains the NSNotification to swap my view controllers. The userinfo: takes a NSDictionary with objects and keys of the subclassed Segue (NSObject) that I created for the empty segues. The ContainerView controller has the observers in it for the notifications that will make the segues happen.
I made a set of parameters in the notification that did the segue. In the receiving view controller, a get parameter method performs an 'if' statement before anything is done to check those parameters and set the label text properly.

Custom UIStoryboardSegue using segueWithIdentifier:source:destination:performHandler:

I am trying to use a custom UIStoryboardSegue to implement a transition between two view controllers. I can do this by subclassing UIStoryboardSegue, and then setting this class in IB. However, I was looking at the docs which say:
If your segue does not need to store additional information or provide anything other than a perform method, consider using the segueWithIdentifier:source:destination:performHandler: method instead.
Implying that you don't need to create the custom subclass, just use the custom performHandler.
I am confused as to where this code should go, and how I go about using it. Do I create the segue as normal in IB and then override that before it is fired (maybe in shouldPerformSegue: or similar). Elsewhere in apple's documentation it says:
Your app never creates segue objects directly; they are always created on your behalf by iOS when a segue is triggered
So I don't quite understand why they are then saying to instantiate a segue using a class creator method.
The point of segueWithIdentifier:source:destination:performHandler:
Provide an alternative to UIViewController performSegueWithIdentifier:sender in cases where you also want to create a custom transition, without creating a segue subclass.
Vend a segue that can be used as the return for segueForUnwindingToViewController:fromViewController:identifier
As noted above, this approach is only viable for segues which you would call manually -- i.e. not for segues that would otherwise be triggered via IB triggers.
So, for example, if you have a segue that needs to be triggered after a certain timeout period (such as a custom lock-screen), you could use segueWithIdentifier:source:destination:performHandler: to handle the custom transition.
-(void)appTimeoutLockScreen
{
UIStoryboardSegue *segue =
[UIStoryboardSegue segueWithIdentifier:#"LockScreenSegue"
source:sourceVC
destination:destinationVC
performHandler:^{
// transition code that would
// normally go in the perform method
}];
// Dev is responsible for calling prepareForSegue and perform.
// Note, the order of calls for an IB triggered segue as well as
// a performSegueWithIdentifier segue is perform first, then
// prepareForSegue:sender. Manual segues need to inverse the call
// in order to ensure VC setup is finished before transition.
[self prepareForSegue:segue sender:self];
[segue perform];
}
Another practical use for the method is unwinding segues. Using a similar scenario
to the previous example, we could use it to return a segue to transition from a lock screen back to the previous viewController:
-(UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController*)toVC
fromViewController:(UIViewController *)fmVC
identifier:(NSString *)identifier
{
UIStoryboardSegue *segue =
[UIStoryboardSegue segueWithIdentifier:#"FromLockScreenSegue"
source:fmVC
destination:toVC
performHandler:^{
// transition code
}];
return segue;
}

Specifying a Delegate before ViewDidLoad in Tabs

So, I followed this tutorial: http://enroyed.com/ios/how-to-pass-data-between-ios-tab-bars-using-delegate/
And the most important part of the tutorial:
- (void)viewDidLoad
{
[super viewDidLoad];
SecondViewController *svc = [self.tabBarController.viewControllers objectAtIndex:1];
svc.delegate = self; //important !! otherwise delegation will not work !
}
The problem is that even if I put it in "viewWillLoad", it still forces me to click on my tab before it initializes. How can I specify this before I click on the tab?
Edit
I have a three tab project. I used that tutorial in the link pass data from tab 1 to tab 2. The data passed is a url from a webview on tab 1 to a url on tab 2. The url gets pass when I click a link on the 1st tab.
The data does get passed, but only if I physically click on the 2nd tab first and then click back to the 1st and click on the link.
So, it appears to me that my code above only runs if I physically click on the 2nd tab.
Your problem is that - until you actually go to tab item 2, secondViewController is not fully initialised, and so there is no data to transfer from vc2 to vc1. In particular, secondViewController's view has not yet loaded, so there is no value to be had from it's slider yet, and no slider, so also no IBAction method to call to trigger the delegate method. Indeed, as the data transfer is only triggered on moving the slider in VC2, it should be fairly obvious that until you go to vc2 and move the slider, nothing is going to happen.
The example you link to uses the delegation pattern, which seems a fairly poor way to deal with your problem. The delegation pattern most comfortably fits with the scenario where there is a hierarchical relationship between delegator (owned) and delegatee (owner) ... not always, but most commonly. In a tab bar controller, the relationships are more like kindred child relationships to the tab bar controller itself.
You haven't offered enough detail in your question as to what you want to achieve, but you need to consider this:
When a tabBarController loads, all of it's child viewControllers are initialised but their views are not loaded.
This means that these methods do get called:
//if loading from a xib or in code
- (id) initWithNibName:bundle
//if loading from a storyboard
- (id) initWithCoder:
- (void) awakeFromNib
But the view loading methods (viewDidLoad, viewWillAppear etc) do not get called as the view does not get loaded unless you actually open the relevant tab.
You could solve this by putting an initialised variable into your viewController2 (in one of the init methods) and accessing that variable via property syntax from vc1. But then, you might as well just put an initialised value directly into vc1. You need to think closely about how each vc is dependent on the other, how you can decouple that dependency, and perhaps how to set up an independent data source that both vcs can access as needed. This could be a model class, or NSUserDefaults, or a property in your appDelegate... just a few of the many possible solutions.

Resources