I seem to be unable to understand how to go about this. I have a button on my main view. This view contains a container view. I would like the button on the main view to make the container view segue to another view. I have set up an identifier for the segue, which goes from containerView1 to containerView2. This is a push-segue. The identifier is pushSegue.
On the button on the main view I have tried this:
- (IBAction)btnChangeLocation:(UIButton *)sender {
UIViewController *a = [[ContainerView1 alloc]init];
[a performSegueWithIdentifier:#"pushSegue" sender:nil];
}
I have successfully performed this segue from within containerView1, by just placing within it, and performing the segue from there. It works just fine then.
- (IBAction)testButton:(UIButton *)sender {
[self performSegueWithIdentifier:#"pushSegue" sender:nil];
}
But how would I go if I wanted to trigger the segue on the containerView1, from the button on the main view?
Thanks.
EDIT:
I would also like to be able to perform the same segue, from a container view, that is within the container view.
Just to summarize.
MainView----->ContainerView1-->pushSegue--->ContainerView2
ContaainerView1 has a subContainerView, which also has a button, which causes ContainerView1 to segue into ContainerView2. This button and the button on the MainView does the same thing really, just from different "locations".
EDIT: Added a picture to help explain. http://tinypic.com/r/maxpp2/8
With UIViewController *a = [[ContainerView1 alloc]init]; you are instantiating a new ContainerView1 controller. That won't help you; you need to call performSegueWithIdentifier:sender: on the instance already created.
Depending on how your Storyboard and code are set up, you need to find a way to get a hold of the embedded view controller.
For this set up:
You could do something like this in the main (hosting) view controller:
#property (weak, nonatomic) IBOutlet UIView *childView;
#property (weak,nonatomic) UINavigationController *container;
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"embedContainer1"]) {
self.container = segue.destinationViewController;
}
}
-(IBAction)doIt:(id)sender {
[self.container.viewControllers[0] performSegueWithIdentifier:#"pushSegue" sender:nil];
}
By implementing prepareForSegue:sender:, you're able to get a reference to the child viewcontroller; cleaner then going through the array of childViewControllers IMHO.
Related
Yes I am new to Xcode and coding in general. I did a bunch of research and figured out how to go from UItableviewcontroller to the viewcontroller then using the "unwind segue" to populate the table, which is not exactly what I want.
I would like the initial scene to be the viewcontroller rather than the table and as such the unwind segue isnt working. I've been trying to rearrange things for days but to no avail.
Any help would be greatly appreciated.
ViewController.m
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.saveButton) return;
if (self.labelField.text.length > 0) {
self.toDoItem = [[YYTData alloc] init];
self.toDoItem.itemName = self.labelField.text;
TableViewController.h
#interface YYYTableViewController : UITableViewController
- (IBAction)unwindToList:(UIStoryboardSegue *)segue;
Tableviewcontroller.m
- (IBAction)unwindToList:(UIStoryboardSegue *)segue
{
YYYFirstViewController *source = [segue sourceViewController];
YYTData *item = source.toDoItem;
if (item != nil) {
[self.toDoItems addObject:item];
[self.tableView reloadData];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.toDoItems = [[NSMutableArray alloc] init];
[self loadInitialData];
Segues are used to navigate through viewControllers that are connected in a UINavigationController stack. To have viewControllers added to this stack, you have to first manually create segues in storyboard. You can access the viewControllers in your navigation stack with methods such as performSegueWithIdentifier:. In order to have your viewController be the first viewController in your navigation stack, do the following in storyboard:
1.) Delete all viewControllers that you have.
2.) Drag and drop a UINavigationController.
3.) Drag and drop a UIViewController (will be your first viewController and I will refer to it as viewController).
4.) Set the class of viewController to the class of your view controller.
--This is done by clicking on the viewController (in the left panel are of storyboard) and navigating to the identity inspector (in the right panel area of storyboard) and entering the name of the viewCotroller's class.
5.) Now you set the rootViewController: of navigationController to viewController by clicking on the navigationController (in the left panel area of storyboard), then looking in the connections inspector (last icon in the right panel of storyboard). Here you will see rootViewController, you'll right click from rootViewController and drag a line from it to your viewController (in the left panel of storyboard).
After completing these five steps, you can add a tableViewController in storyboard create a segue for it, and everything should work as you'd like it to.
I have a table controller in which I use didSelectRowAtIndexPath to navigate from the pushed cell to another view. In it I initialize new view controller and push some data in it. After that I do pushViewController.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
ServicesModel *service = [services objectAtIndex:indexPath.row];
ServiceViewController *serviceViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ServiceView"];
serviceViewController.serviceModel = service;
NSLog(#"Set model %#", service.title);
// Pass the selected object to the new view controller.
[self.serviceController pushViewController:serviceViewController animated:YES];
}
In my ServiceViewController I have a label serviceTitle and ServiceModel property for selected service
#property (weak, nonatomic) IBOutlet UILabel *serviceTitle;
#property (strong, nonatomic) ServiceModel *serviceModel;
Using viewDidLoad I'm trying to change text of the label
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"viewDidLoad %#", self.serviceModel.title);
self.serviceTitle.text = self.serviceModel.title;
}
Also I'm trying to access model in viewDidAppear
- (void) viewDidAppear:(BOOL)animated
{
NSLog(#"viewDidAppear %#", self.serviceModel.title);
}
but when view opens, label is empty. Why? What am I doing wrong? The most strange is the log:
(-[ServiceViewController viewDidLoad]) (ServiceViewController.m:43) viewDidLoad (null)
(-[ServicesTableViewController tableView:didSelectRowAtIndexPath:]) (ServicesTableViewController.m:127) Set model Google.com
(-[ServiceViewController viewDidAppear:]) (ServiceViewController.m:36) viewDidAppear (null)
It shows that viewDidLoad fires before I assign the model property. And in viewDidAppear model property is still null. How it can be?
You have two problems. The first one, as 0x7fffffff mentioned, is that you're instantiating your controller incorrectly (it should be initWithNibName:bundle: if made in a xib, and like 0x7fffffff said if in a storyboard).
Second, you can't access the label in serviceViewController from didSelectRowAtIndexPath, because its view has not been loaded yet. So, instead of setting the label in didSelectRowAtIndexPath, you should have a string property in serviceViewController, and give it the value service.text. Then in viewDidLoad, you can populate your label with that string.
Is the label missing altogether, or do you see it and it just didn't receive updated text? If the label is missing, then it's probably a problem in how you're creating the view controller. If for example, you're using storyboards, you should be accessing the view controller like this:
ServiceViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"SomeStoryBoardID"];
Instead of this:
ServiceViewController *serviceViewController = [[ServiceViewController alloc] init];
If however, you can see the label, but it just hasn't updated it's text, the first thing you should to is examine the connections inspector in Interface Builder, and verify that the IBOutlet for the label is properly linked.
I have a problem on how to properly do a certain kind of action.
The image below shows a UIViewController, but the second part of the view is a custom UIView (the one with the profile pic, name and Show View button).
The subclassed UIView is allocated using this code:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
[self.view addSubview:profileView];
The problem is of course, that the button on the UIView can't show any view, since it's only the UIViewController that can push another ViewController to the window(correct?).
The UIView is used in several places in the app and needs to be added easily and have the same behavior across the application.
This worked great until I added the button, and I'm starting to think I've made this wrong, and there has to be a better way to do it (maybe change the UIView to something else?).
I was thinking I should be able to call:
self.superview
And then somehow get the ViewController so I can push another ViewController into the view hierarchy, but nope.
Any suggestions and a tips on how to do this correctly?
UPDATE:
I have no idea on how to push another UIViewController from the button.
What should I do in this method when pressing the button in the UIView:
- (void) showViewButtonTouched:(UIButton*)sender {
GPProfileSocialFriendsViewController *friendsSettings = [[GPProfileSocialFriendsViewController alloc] init];
}
How do I push GPProfileSocialFriendsViewController?
Your - (void) showViewButtonTouched:(UIButton*)sender method should be in your controller and would probably be better named - (void) showView:(UIButton*)sender or - (void) showProfile:(UIButton*)sender so it clearly denotes what it does (not how you got there).
It's not the view's responsibility to manage transitions from a state to another. If you move your method to your controller, your problem is no more (you can easily access self.navigationController or push directly if you don't have an navigation controller like this:
[self presentViewController:vcThatNeedsToBePushed animated:YES completion:nil];
I think you can create weak reference in GPProfileView on UIViewController. Like this:
#property (weak, nonatomic) UIViewController *rootController;
when you create GPProfileView, assign rootController-property:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
profileView.rootController = self; // if view created in view controller
[self.view addSubview:profileView];
and in implementation of button selector:
self.rootController push... // or pop
May be this not correct, but you can try
You could let the view controller push the next view controller when the button is pushed. The view controller can add a target/action on the button, so that the action method in the view controller is called on the touch up inside event.
I have a storyboard app with two different ViewController. On the second ViewController I have a UIImageView and I would like to call that UIImageView on the first one. I've been looking for a solution but I can't find anything that work for me.
#property(nonatomic, retain) IBOutlet UIImageView *pic;
I just want to be able to use that UIImageView on the other viewcontroller. I hope you can help me.
EDIT:
yes you can try to set your UIImageView in prepare for segue. I have tried it and it seems to work. You would do it as follows:
**I am assuming that the controller you are seguing to is called NewViewController
**I am also assuming your NewViewController has a UIImageView called imageView
-(void)prepareForSegue....
{
if ([segueIdentifier isEqual....])
{
NewViewController *newController=(NewViewController *)[segue destinationViewController];
[[newViewController imageView]setImage:self.someUIImageFromCurrentClass];
[[newViewController imageView]setNeedDisplay];
}
}
and yes, you do have to create a UIImage in current class (in this case it's self.someUIImageFromCurrentClass). That's the point is that you're taking an image from current ViewController (current class) and showing it in the ViewController you are seguing to.
ORIGINAL:
-(void) prepareForSegue: (UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"your segue name"])
{
[segue.destinationViewController setYourUIImage:self.someImageFromCurrentClass];
}
}
be sure to import the .h file for the controller you're seguing to.
second, YourUIImage is an UIImage that is publicly declared in the controller you're seguing to (like Michael mentioned).
In the destination controller, you'll want to grab that UIImage and set it as the image for you UIImageView:
[self.myImageView setImage:YourUIImage]; // <-- you can do this in ViewDidLoad or ViewWillAppear
that should get you going
**Also I guess it's worth mentioning is that a segue always creates a new instance of the controller you're seguing to, which doesn't exist before the segue, which is why you have to pass the image to it.
You can't use outlets between view controllers (VCs) because there's no automatic one for one VC to refer to another. You'll have to expose public properties or methods that allow the manipulation you need and find some way to pass a reference to one VC from another.
For example, if you pass from one VC to another via a segue, you can manipulate properties in the destination VC from the source in prepareForSegueWithIdentifier:.
I know this question has been asked several times and I did read existing posts on this topic but I still need help.
I have 2 UIViewControllers - parent and child. I display the child UIViewController using the presentModalViewController as below:
ChildController *child =
[[ChildController alloc] initWithNibName:#"ChildView" bundle:nil];
[self presentModalViewController:child animated:YES];
[child release];
The child view has a UIPickerView. When user selects an item from UIPickerView and clicks done, I have to dismiss the modal view and display the selected item on a UITextField in the parent view.
In child's button click delegate, I do the following:
ParentController *parent =
(ParentController *)[self.navigationController parentViewController];
[parent.myTextField setText:selectedText];
[self dismissModalViewControllerAnimated:YES];
Everything works without errors. But I don't know how to load the parent view so that it displays the updated UITextField.
I tried
[parent reloadInputViews];
doesn' work. Please help.
Delegation is the way to go. I know some people that may be looking for an easier solution but trust me I have tried others and nothing works better than delegation. So anyone having the same problem, go read up on delegation and follow it step by step.
In your subviewcontroller.h - declare a protocol and declare delegate mthods in it.
#protocol myDelegate
-(void)clickedButton:(subviewcontroller *)subController;
#end
In your subviewcontroller.h, within #interface:
id<myDelegate> delegate;
#property (nonatomic, assign) id<myDelegate> delegate;
NSString *data;
-(NSString *)getData;
In your subviewcontroller.m, synthesize myDelegate. Add the following code to where you want to notify your parentviewcontroller that the subview is done doing whatever it is supposed to do:
[delegate clickedButton:self];
and then handle getData to return whatever data you want to send to your parentviewcontroller
In your parentviewcontroller.h, import subviewcontroller.h and use it's delegate
#import "subviewcontroller.h"
#interface parentviewcontroller : VUIViewController <myDelegate>
{}
In your parentviewcontroller.m, implement the delegate method
- (void)clickedButton:(subviewcontroller *)subcontroller
{
NSString *myData = [subcontroller getData];
[self dimissModalViewControllerAnimated:YES];
[self reloadInputViews];
}
Don't forget memory management!
If a low-memory warning comes in during your modal view's display, the parent's view will be unloaded. Then parent.myTextField is no longer referring to the right text field until the view is reloaded. You can force a reload of the view just by calling parent.view;
However, a better idea might be to have the parent view have a String property that can be set by the child view. Then, when the parent view reappears, put that data into the text field, inside viewWillAppear: for example. You'd want to have the value set to some default value for when the parent view initially shows up too.
-(void) viewWillAppear:(BOOL) animated doesn't get called for me either, exactly when it's a modal view controller. No idea why. Not incorrectly overridden anywhere in this app, and the same problem occurs on the other 2 apps I'm working on. I really don't think it works.
I've used the delegate approach before, but I think that following approach is pretty good as well.
I work around this by adding a private category to UIViewController, like so:
.h file:
#interface UIViewController(Extras)
// returns true if this view was presented via presentModalViewController:animated:, false otherwise.
#property(readonly) BOOL isModal;
// Just like the regular dismissModalViewController, but actually calls viewWillAppear: on the parent, which hasn't been working for me, ever, for modal dialogs.
- (void)dismissModal: (BOOL) animated;
#end
and .m file:
#implementation UIView(Extras)
-(BOOL) isModal
{
return self == self.parentViewController.modalViewController;
}
- (void)dismissModal: (BOOL) animated
{
[self.parentViewController viewWillAppear: animated];
[self dismissModalViewControllerAnimated: animated];
}
#end
which I can now call like this when I want to dismiss the dialog box:
// If presented as a modal view, dismiss yourself.
if(self.isModal)
[self dismissModal: YES];
and now viewWillAppear is correctly called.
And yes, I'm donating a bonus 'isModal' property, so that the modal view can tell how it was being presented, and dismiss itself appropriately.