iOS >> prepareForSegue >> IBOutlet Update Doesn't Work? - ios

I'm trying to update a Label in the 2nd VC from the 1st VC within the prepareForSegue method.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
MYSecondViewController* secondVC = (MYSecondViewController*)segue.destinationViewController;
secondVC.titleLabel.text = #"First VC Says: You Are Second!!"; //This doesn't work
secondVC.dataPassString = #"First VC Says: You Are Second!!"; //This works + secondVC viewDidLoad
}
If I update the Label directly, it doesn't work.
If I update a String Property and then assign it to the Label in the Second VC viewDidLoad, it does work.
Does it mean that upon prepareForSegue call the second VC viewDidLoad method was not called yet?
Was some init method called (so the NSString object could pass)? If yes, which one?
Is there a way to update IBOutlets in the 2nd VC from the 1st VC?

The short answer is: Don't do that.
You should treat another view controller's views as private and never try to manipulate them. It breaks the OOD principle of encapsulation.
What you want to do is to add (string or other type) properties to your destination view controller, and set THOSE in prepareForSegue. Then in your destination view controller's viewWillAppear method, copy those property values into the view controllers' views.
In your case, the datePassString property is exactly what you want.
That way, if you change the structure of your second view controller down the road, and decide to display the information to a different view, you don't break the link between the 2 view controllers. Your destination view controller can still fetch the data from it's source, and do something different with it.
P.S. as the other poster said, the reason setting secondVC.titleLabel.text fails is that in prepareForSegue, the destination view controller's views haven't been loaded yet.

If you add in this line in your "prepareForSegue" method:
if(!secondVC.titleLabel)
NSLog(#"titleLabel is null and it likely hasn't been loaded yet")
You'll see that the view hasn't been loaded until it's time for it to appear (which happens after prepareForSegue). That's why the datePassString property you're using is working while the IBOutlets are null until the view is loaded.

Related

How to wait for a UIViewController to be completely initialized?

I have a custom segue type (overriding init and perform methods of UIStoryboardSegue) and in init method I instantiate the destination view controller(VC). In prepareForSegue method of source VC I call a method of the destination VC that tries to reload the tableView of the destination VC. The problem is that the table view is not always initialized and I SOMETIMES get a nil de-reference error when I call the reloaddata of the tableview.
The question is that how can I wait till the VC is fully initialized and do not get this error?
I am using swift and would appreciate if you write any sample code for the answer in swift.
just make a call on the viewController's view to force its load.
[viewController view]; //will force a loadView if necessary
///then do what you're trying to do..
I think that the best approach in this case is to add a flag property in the destination VC, something like:
var forceReload: Bool
that you set from prepareForSegue in the source VC. This way, you can choose where to actually perform the data reload from the destination VC (for example, in viewDidLoad or viewWillAppear) by simply checking the value of that flag - of course if the flag is true, don't remember to reset it.
If you also need to pass data from the source to the destination VC, use one or more properties declared in the destination and set from the source.

Pass values to previous view controller in a navigationviewcontroller stack

In UINavigationViewController, if I wanna pass values from one controller to next, just call - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender , but what should I do if I wanna pass values from one viewController to previous viewController
I remember running across this same issue a few projects back. I can't find the my code to answer this question, but I did find a few tutorials.
http://prateekvjoshi.com/2014/02/16/ios-app-passing-data-between-view-controllers/
http://www.infragistics.com/community/blogs/torrey-betts/archive/2014/05/29/passing-data-between-view-controllers-ios-obj-c.aspx
and hence the concept of delegate came forth from segues.
Basically Segues are transition from one view to another but the child view is over the parent view, (inside a stack) so the parent view is still loaded.
So if u put segues everywhere and pass values between them then objects will keep on be creating and stored inside a stack and thus the cycle carries on.
So delegates was introduced.
Delegate is a method by which a child view controller(the later one) sends information using the inbuild delegate methods or self created protocol methods to the Previous view controller(the first one).
Here the one sending the information(later view) declares a delegate object, and a delegate method.
Which is then implemented by the recieving class(first view). So even after the later view is popped from the stack, the information is sent back to the root view by the delegate method.
Go through the documentation, its given in a more appropriate way
Hope this helps
Set previous view controller as delegate of current view controller and pass any values you want. This is standard approach.

How to use segues

I'm creating an app in Xcode that currently consist of a Navigation Controller a Table View Controller and a regular View Controller.
I'm using StoryBoard and have created a segue between the table view and the regular view controller. In the navigation bar I have a button that I've dragged to the view controller in the StoryBoard. When I click at the button, the new View Controller is viewed like it suppose to. I then tried to pass data from the table view controller, see below:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"läggTill"])
{
// Get reference to the destination view controller
AddPlayerViewController *apv = [segue destinationViewController];
[apv.myTextField setText:#"hello"];
}
}
The segue identifier is "läggTill", but the code inside the if-statement is not executing.
Two questions:
What is wrong with this approach?
Is this the best approach when using StoryBoards? Can I pass data via viewWillDisappear?
Can I use segues to pass data back to the table view controller from the view controller?
It makes all the sense in the world to pass your data in the prepareForSegue:sender:. I would not recommend passing data in viewWillDisappear because it just makes it messy and it reduces readability + it becomes harder to keep track.
I think your string comparison is not working! Put an NSLog for your identifier string see on the console. I have a feeling it might not like the "ä" character in comparison.
About your last question, as Gabriel.Massana pointed out, for passing data back, using delegate is the way to go.
Note 1: Another problem I noticed is a possible typo you might have "apv" and "avc".
Note 2: Another reason for it failing is that you are setting the TextField before viewDidLoad gets called on your destination controller. I suggest that pass it as string and in the viewDidLoad of your destination, set the text to your TextField.

prepareForSegue not called in embedded segue

I have a table view controller embedded in a container in a view controller.
In both the view and table view controllers prepareForSegue method I put NSLog(#"name of the controller")
I see a log for the view controller but not the table view controller. Shouldn't I also see the nslog for my table view's prepareForSegue?
Exactly - as Greg explains, the embed type of segue is
only called during setup!
This is very confusing. You could say that
"prepareForSegue" is A REALLY BAD NAME!
the name "prepare for segue" only makes sense in the (rare!) case where you are actually "segueing" from one scene to another
In an iOS app container views are commonplace, you have them everywhere, whereas you rarely use an actual "scene segue".
So really, "prepareForSegue" should be called something like:
"Hey, we're setting up all your container views -- you can grab any info you need at this time! Oh, if you happen to be doing a scene segue, you can use this also!"
what about:
containerViewBeingSetUpOhAndAlsoPrepareForSegueIfYouHappenToBeDoingThat:
That's a bit long, but clearer!
It's just one of those weird things about iOS that you have to know, but is never explained anywhere.
Here's a full explanation of using container views for beginners https://stackoverflow.com/a/23403979/294884
In Your Log Frame View Controller prepareForSegue will be called directly after initWithCoder: to prepare your TableViewController. I cannot see your connection between table view controller and another view (view on the right) but I believe it will be called when you hit the row.
//EXTENDED
The other thing could be that you haven't add UINavigationController on the view hierarchy and you set up your segue style to 'push'. Try change style of your segue to 'modal' in attribute inspector.
Hope this help.
Other than what's already discussed, you should make sure you aren't ignoring segue identifier in following delegate call.
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender

Why can't I change text of Text View of child VC from parent VC?

I'm working on an app that I need to change the text of text view of child view controller from a method in the parent view controller.
I have a button in child VC, when it's pressed, the view doesn't change, but a method in parent VC is called, and another method in parent VC will change the text of text view in child VC.
[self.delegate buttonPressed];//in child VC, call method in parent VC
It was working before I use storyboard.
In the parent vc, I just had code below,
childViewControler.textViewName.text=#"something";
Now I changed to storyboard, by using NSLog, I know the method to change the text is called. But no text is displayed.
I'm thinking I might add something since I am using storyboard now?
Can someone give me advice?
Thank you.
The childViewControler variable in your parent view controller needs to point to the instance of your child view controller class created by the segue. If you don't assign anything to it, it's nil, and any messages to it do nothing.
It's unclear from the code you've posted whether it's a property, instance variable, or local variable; here's how you could do it with an instance variable:
#implementation ParentViewController
{
ChildViewController *childViewController;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// if you have multiple segues, check segue.identifier
// so you only do this for the correct segue
childViewController = segue.destinationViewController;
}
- (void)buttonPressed
{
childViewController.textViewName.text=#"something";
}
#end
The key bit is in prepareForSegue:sender: -- you need to make sure that the childViewController variable points to the view controller created by the segue.
It's also possible that your problem results from still having some old pre-storyboard code that allocs and inits a child view controller and puts it into your childViewController variable. In that case, you'd have two instances of that view controller class around: the one created by the segue and the one you made. Only the one created by the segue is hooked into the UI, so any changes you make to the other won't be visible. You can solve this problem by getting rid of any code that creates and presents the child view controller -- the storyboard takes care of all that now, and prepareForSegue:sender: is the only place you need to get a reference to the child view controller.

Resources