Storyboard relationship in iOS - objective-c

I'm attempting to use the new Storyboard feature, however I am confused about Storyboard Relationships? How are they different to IBOutlets?
Also how can I add my own relationship, to my own UIViewController subclass?
I have tried looking in the documentation but can't find much about them.

The way I understand it is: relationships are iOS's way of representing a 'parent-child' relationship and while that does seem quite recursive, an example of parent-child relationship is a UIViewController Container containing a UIViewController.
That's the theory anyway -- it's better understood using the UINavigationController. It is called a 'container' because it contains as many regular UIViewControllers in a 'stack' metaphor so you can do your normal UITableView drill downs and pop offs.
The key point is that the segue between UINavigationController and the first UIViewController in your stack, there is a 'relationship' while the segue between all the rest of the UIViewControllers is just a regular push segue.
The same thing is evident in the UISplitViewController -- it needs two view controllers (sometimes called content view controllers) from launch and these are connected up between the parent UISplitViewController (the container) and two regular (content) view controllers
(thus relationships are not like IBOutlets, but more like segues -- they are even in the 'segues' category of the standard view controller containers)
Now - we aren't allowed to subclass the standard view controller containers, but we are allowed to create custom view controller containers, but I can't for the life of me define a relationship in my custom view controller container!!!!!!!!!!!
so: "can I use them in my own controllers?" the answer is yet unknown (to me at least, and the documentation is thin at best)

Create a subclass of the UIStoryboardSegue like this:
#implementation JTARelationshipSegue
- (void)perform
{
return;
}
#end
Make a custom segue between your two objects and set the class as JTARelationshipSegue. In your view controller make the view controller perform the segue like this:
- (void)viewDidLoad
{
...
[self performSegueWithIdentifier:#"addChild" sender:self];
}
You need to have set the segues identifier in interface builder to addChild.
Impement prepareForSegue:sender so that it adds the segues destination view controller as a child of the current view controller, like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destination = [segue destinationViewController];
[self addChildViewController:destination];
...
}
You will need to have a different segue identifier for each child that you want to create (or another way of identifying the specific view controller.
This will make your storyboard file look prettier, and more readable, however you may do better to just load your other view controllers and add them as children in your view did load method:
- (void)viewDidLoad
{
[self addChildViewController:
[[self storyboard]
initiateViewControllerWithIdentifier:#"myIdent"]];
...
}

I wrote a few tutorials on how to use storyboards over on my site...
Part 1 of tutorial
Part 2 of tutorial
Perhaps that will help a little bit? In essence, the Segue relationships provides an identifier for the link between two items on your storyboard. You can use these identifiers to manage how things work.

Related

custom container view controller + storyboard?

I want to implement my own container view controller.
Pls, imagine that this is my ipad
Kind of usual split view controller, but I want to extend it: width of view "a" should be resizable, when I tap view 'B' - view 'C' goes to visible area
For instance in my storyboard, I have 3 container views: AViewController (for A view), BViewController,CViewController and one combined view controller (initial view controller).
In combined view controller i will implement embed segues from container view and initialize relationship between container views via following code:
#property ... *aViewController,*bViewController,*cViewController;
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender
{
if ([segue.identifier isEqualToString:#"EmbedAViewController"])
{
self.aViewController =
segue.destinationViewController;
}
if ([segue.identifier isEqualToString:#"EmbedBViewController"])
{
self.bViewController=segue.destinationViewController
}
-(void)viewDidLoad
{
[super viewDidLoad];
self.aViewController.bViewController=self.bViewController;
}
Question1: Is it proper way implement my assignment using storyboards?
Question2: What kind of limitations does container view have? Is it a visual replacement of addChildViewController API? If no, where should I implement child-parent relationship? I should use in my combined view controller in prepareForSegue method
[self addChildViewController:aViewController];
[self.view addSubview:aViewController.view];
Question3: How to put container view outside of visible area at the beginning?
If I somewhere did a mistake or have a big misunderstanding of basic concepts, do not beat me. I did a lot of google-foo, I would really appreciate any help. Great thanks in advance!
Edit:
I want to set up relationship between all of them. "B" view's content depends on "A", and "C" view's content depends on "B".
I think you do have some misconceptions. If you want to implement container view controllers in a storyboard, you don't need to do anything in code. Start with one controller, the one you're calling combined view controller, and drag in 3 container views. Initially, you might want to size them so they are all full height, and make them all fit side by side in the main view. You can then change their sizes and positions using the size inspector, so that C's view starts at the right edge of combine controllers view, that way it will be off screen to start with. You will automatically get three view controllers connected to their respective container views with an embed segue. All three of these controllers will be instantiated at the same time as combined controller. You will need outlets in combined controller to each of its container views, so that you can resize them as necessary in code.

IOS Accessing one view from another view

Basically I have a view controller having two subviews. I want these views to be connected. A touch event should trigger an event from another view and vice versa. I have thought about two solutions.
1-) Accessing views through their view controllers
2-) Each view has a pointer to another view
I am a newbie on IOS and as far I read from other problems it is mentioned that accessing view controller from a view is not suggested. So, what do you guys suggest me to do?
Edit:
I didn't make much progress on coding but my first view is:
#interface PaintView : UIView
-(id)initWithFrame:(CGRect)frame andController:(ViewController*)ctrl;
and i will control the touch event and access my viewcontroller:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//[self.controller somethingThatAccessToOtherView]
}
and second view will be very similar to that one.
Why dont you use the delegate pattern!
For the two views communicate with each other ..
Its essentially similar to " Each view has a pointer to another view" but in a much more flexible manner
here is a so thread on it and
If you want the simplest, dirtiest, and non recommended way of proceeding. Tag your two views with some sort of unique tag (use say, -20 and -21, or something. So in the view controller where you create the views you do the following.
[v1 setTag:-20];
[v2 setTag:-21];
Then you can do from in say, v2, the following.
[self.superview viewWithTag:-20];
to get a reference to v1. However, this assumes the same superview, and is not a nice way of doing things unless you are sure that the view heirachy will not change (I'm talking, it's a widget you made that no one else is going to touch and you've documented it well anyway).
A better way would be to use a delegate pattern. The ViewController is the delegate of each of the subviews. On touch, the subviews call some method like
[delegate iwastouched:self];
and the delegate has a method like
-(void) iwastouched:(UIView *) someview {
if(someview == v1){
//view 1 was touched, do something to view two
}
if(someview == v2){
//view 2 was touched, do something to view one
}
}
Another bad way of doing it would be to use notifications. Hell, there are about as many ways to do this as you could like. Some are just not as nice.
Instead of doing this, in the subclass of UIViewController, you should have two members. These member will represent each of the subview.
Now, inside the subclass of UIViewController, add the methods of touch which are of your interest. Inside this methods, identify the view on which any touch event is generated.
Depending on it, pass the event to other view.
You should not have UIViewController as a iVar of your UIView subclass.

Passing Data(String) from Child VC to Parent VC using easiest Way

Passing Data(String) from Child View Controller to Parent VC using easiest Way .
i tried a Couple of ways , but got lost , can some one tell me the best way .
Srikanth is correct. If you have a segue from a view controller (our "first" view controller) to another (our "second" view controller), all you need to do is to create a property in the second one that points to the first one. You will just have the first view controller make sure to set that pointer before it performs the transition. Having done that, the second controller can update properties or invoke methods of the first controller.
For details, see Configuring the Destination Controller When a Segue is Triggered in the View Controller Programming Guide for information on how to set a property in the second view controller (in this case, that property will be a weak pointer to the first controller) in prepareForSegue. Then, as Srikanth says, the second controller can use that pointer to update properties in the first one.
So, in your second view controller, have a property (note carefully, it should be weak) that points back to the first view controller:
In SecondViewController.h:
#class FirstViewController;
#interface SecondViewController : UIViewController
#property (nonatomic, weak) FirstViewController *firstViewController;
#end
Then in the FirstViewController.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"YourSegueIdentifierHere"])
{
[segue.destinationViewController setFirstViewController:self];
}
}
So, if your first view controller had, for example, a property of favoriteColor:
#interface FirstViewController : UIViewController
#property (nonatomic, strong) NSString *favoriteColor;
#end
Then, the second view controller could use its firstViewController property to update this favoriteColor, like so:
self.firstViewController.favoriteColor = #"Blue";
Clearly:
Replace FirstViewController and SecondViewController with the appropriate class names;
Make sure that your second view controller's .m file does an #import of the first view controller's .h; and
Make sure you've specified a segue identifier in Interface Builder for your segue from the first controller to the second one and adjust the prepareForSegue above, replacing YourSegueIdentifierHere with your identifier.
In iOS 6, you can also accomplish this via an unwind segue. You'd just have the prepareForSegue of the second view controller update the property of the unwind segue's destination controller (i.e. the first controller). What's nice is that unwind segues can go back an arbitrary number of levels, so for more complicated scenarios, it's very nice. It is iOS 6, or higher, only, though.
To do unwind segue's, first you must define an unwind action in the first view controller (identified as such by the combination of the IBAction return type and the UIStoryboardSegue parameter), e.g.:
- (IBAction)done:(UIStoryboardSegue *)segue
{
// do any clean up you want
}
Then, the second (or third or ...) view controller can create an unwind segue by control-dragging in Interface Builder from a button to the exit icon in the scene's dock. You can have the controller from which you're unwinding do the logical prepareForSegue to pass information back to the first view controller.
By the way, you used the terms "parent" and "child", but I wanted to make it clear that I assumed that you were not talking about the more advanced topic of view controller containment, in which a view controller is invoking other view controllers to facilitate the presentation of a single screen of information (as opposed to transitioning between different scenes in an app). As rdelmar notes in our comments below, the terms "parent" and "child" controllers, strictly speaking, more properly imply that one is using view controller containment.
Obviously, if you are using view controller containment, then clearly the discussion of segues, prepareForSegue, etc., don't apply. Furthermore, a properly implemented container view controller guarantees that the child controllers can actually use the UIViewController property of parentViewController, without needing to define our own property to reference the parent. All you need to do is to cast/define parentViewController to be the right subclass, and then you can access your subclassed properties very easily.
Best way is to have a property in the child view controller of type id or UIViewController. Set that value to parent view controller when you are creating the child.
Once you have access to parent view controller in the child view controller you can access its methods or properties.

UIViewController within a UIViewController

So I have a viewControllerA, and I want to add another View managed by viewControllerB to it. There is only one UISlider activating a simple action in viewControllerB. It won't crash if I don't touch this UISlider, it will once I use UISlider. I am using ARC. I am using:
[self.view addSubView: viewControllerB.view];
to add viewControllerB to viewControllerA. Am I missing something? Thanks.
OK. It looks like a really simple situation. I just added one view controller and one action. Here is the demo project code on github: https://github.com/randomor/Demo
The reason why I want this to work is because I have another app that will create a view controller on the spot and add it to anther view. And I don't want to do it modally, because I don't want the new view controller to cover the whole screen. Thanks.
SOLUTION: So I'm now just using the latest ViewController containment API:
[self addChildViewController:viewControllerB];
It works! as long as I added this line, the event will be passed to its own controller and it stopped crashing.
i recommend you, to use the following code
in ViewControllerA.h
#import "ViewControllerB.h"
in ViewControllerA.m (where you want to push the new controller)
ViewControllerB *newController = [[ViewControllerB alloc]init];
[self presentModalViewController:newController animated:YES];
in ViewControllerB.m you will need
[self.presentingViewController dismissModalViewControllerAnimated:YES];
to make it vanish again.
concerning multiple controllers for one open screen (Apple ViewController Programming Guide):
Each custom view controller object you create is responsible for managing exactly
one screen’s worth of content. The one-to-one correspondence between a view controller
and a screen is a very important consideration in the design of your application.
You should not use multiple custom view controllers to manage different portions
of the same screen. Similarly, you should not use a single custom view controller
object to manage multiple screens worth of content.
You should try and avoid the practice of nesting UIViewControllers. While it is technically supported in iOS5, it is ill-advised, for many reasons, including the type of problem that you're having (you have a dangling pointer to a UIViewController, which is why you are crashing).
http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/
Although this question is extremely vague, I imagine that you are not keeping a reference to View Controller B, and so when view B tries to interact with it, it causes EXC_BAD_ACCESS.
What's the object that is set as the target for the slider? If it's a EXC_BAD_ADDRESS, then you may not be retaining the target, most probably the view controller for the slider.

how to implement didSelectRowAtIndexPath in table view in Xcode 4.2?

SliderDemoController *sliderDemoController=[[SliderDemoController alloc] initWithNibName:#"" bundle:nil];
[self.navigationController pushViewController:sliderDemoController animated:YES];
i implemented this method using storyboard. but this method is not working. i want to open new controller in table view cell.
If You want to access any view controllers which is in story board then you should use UIStoryBoard class.If you not sure about how to do that so, Here is the link for apple document about UIStoryBoard and use the method + (UIStoryboard *)storyboardWithName:(NSString *)name bundle:(NSBundle *) storyboardBundleOrNil to access your storyboard.
Access your view controller with the method - (id)instantiateViewControllerWithIdentifier:(NSString *)identifier and use the returned view controller object.Hope this helps you....
You don't need to query the UIStoryboard object -- setting up transitions between view controllers is what storyboards do automagically. And with storyboards, you don't need to implement tableView:didSelectRowAtIndexPath: either.
If you haven't already, define a segue from the (prototype) table cell to the destination view controller (looks like that's SliderDemoController for you) by control-dragging. (Choose the Push segue type.) For functionality equivalent to the non-storyboard code you posted, that's all there is to it -- now tapping any row in the table will push in a SliderDemoController.
However, you probably want to configure that SliderDemoController based on which row was selected, right? Then, in your table view controller, implement prepareForSegue:sender:. There you can get a reference to the destination view controller (from the segue parameter) and set it up however you like.