App crashes due to unrecognized selector sent to VC - ios

My app crashing due to unrecognized selector sent with segue. I know it's a common question in stackoverflow. I tried all the solution but could not get through this. I think i'm missing something. My current project throws this exception, but a new project with this same code does not throw any exception. Why ? I tried like below code snippet to pass data from FirstViewController to DetailViewController using segue.
In my FirstViewController.h file :
#import <UIKit/UIKit.h>
#import "DetailViewController.h"
#interface FirstViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
#end
In my FirstViewController.m file :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//if ([segue.identifier isEqualToString:#"detail"]) {
DetailViewController *vc = (DetailViewController *)[segue destinationViewController];
vc.nameStr = #"Nuibb";
//}
}
In my DetailViewController.h file :
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController
#property (nonatomic, strong) NSString *nameStr;
#end
In my DetailViewController.m file :
#import "DetailViewController.h"
#interface DetailViewController ()
#property (weak, nonatomic) IBOutlet UILabel *label;
#end
#implementation DetailViewController
#synthesize nameStr;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.label.text = nameStr;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
And i'm getting this error message in log -
2015-03-11 10:27:24.145 bdipo[1185:18722] -[UINavigationController setNameStr:]: unrecognized selector sent to instance 0x7fe928dad8c0
2015-03-11 10:27:24.147 bdipo[1185:18722] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController setNameStr:]: unrecognized selector sent to instance 0x7fe928dad8c0'

You only need one Navigation Controller in any app which loads at launch time and will handle navigating the full stack of view controllers for you. It needs to be the root view controller, with the "Is Initial View Controller" box checked (under Attributes Inspector, View Controller) to give it that start-arrow pointing in to the left side of the view.
Assuming your NewsTVC is the first view controller you want to show, delete the navigation view controller in your storyboard. Select NewsTVC, go up to your XCode toolbar and select "Editor --> Embed In --> Navigation Controller".
A navigation controller will be created that segues into your NewsTVC. Make sure "Is Initial View Controller" is selected for the new Navigation Controller, and drag a new segue between you NewsTVC prototype cell and the DetailVC. This should organize your stack so the segue runs properly.

You can access your view as below
DetailViewController *detailVC =
[self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];//modify this identifier name as per your StoryBoardIDentifier for detailview at storyboard..
detailVC.nameStr=#"Nuibb";
[self.navigationController pushViewController:detailVC animated:NO];//if you want to push to detail view from first view
Hope it helps you...

V1(segue name = xyz)-->NavController-->(ROOT)VC2
In your vc1's didselectforRowAtIndexPath or any other Action method write this code.
[self performSegueWithIdentifire:#"xyz"];
Then override the method -
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"xyz"]) {
DetailViewController *vc = (DetailViewController *)[segue destinationViewController];
vc.nameStr = #"Nuibb";
}
}
Inside DetailViewController.h declare a property first.
#property(nonatomic,strong)NSString *nameStr;

On tableView didSelectRowAtIndex a UINavigationController is segued as per your storyboard and your navigationController is not having any property named nameStr
You will have to segue detailViewController direct. As UINavigationController cannot pe pushed in navigation.

Related

Objective-C - Reloading table view data after dismissing modal

I am trying to reload my table view controller after inserting an item using core data. I am using storyboards to present my modal view controller and am setting the delegate of the destination view controller in prepare to segue but i believe that is the issue, as my didAddCollection method is not being triggered. I am not sure what i am missing here. i used iOS TableView Reload after dismissing modal as the base for the current code i have.
In my CCNewCollectionViewController.h
#protocol NewCollectionDelegate <NSObject>
-(void) didAddCollection;
#end
#interface CCNewCollectionViewController : UIViewController {
id delegate;
}
#property (nonatomic, retain) id <NewCollectionDelegate> delegate;
In my CCNewCollectionViewController.m
#implementation CCNewCollectionViewController
#synthesize delegate;
// save data
[self.delegate didAddCollection];
// dismiss view controller
In my CollectionTableViewController.m
#interface CollectionTableViewController () <NSFetchedResultsControllerDelegate, NewCollectionDelegate>
-(void) didAddCollection {
[self.tableView reloadData];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[[segue destinationViewController] setDelegate:self];
}
A very silly mistake. When setting the delegate of the destination view controller, i was actually setting the delegate of the UINavigationController instead of the CCNewCollectionViewController.
UINavigationController *nc = [segue destinationViewController];
CCNewCollectionViewController *vc = nc.viewControllers[0];
[vc setDelegate:self];
I changed my prepareForSegue code to this and it solved my problems!
Not sure if this will solve your problem, but I think it is more typical to just create a weak delegate property. No need to synthesize.
#interface CCNewCollectionViewController : UIViewController
#property (nonatomic,weak) id <NewCollectionDelegate> delegate;
...
#end
See here for more information about working with protocols.

Delegate methods not responding

I'm having some trouble getting a viewcontroller to respond to a action inside a modal viewcontroller.
I have a sidebar with a settings button. This button presents a settings viewcontroller modally, and I want my sidebar to respond when a user taps the log out button on my settings viewcontroller.
In my settingsTableViewController.h I set up the delegate like this:
#class SettingsTableViewController;
#protocol SettingsTableViewControllerDelegate <NSObject>
- (void)loggedOutUser:(SettingsTableViewController *)viewController;
#end
#interface SettingsTableViewController : UITableViewController
#property (nonatomic, weak) id <SettingsTableViewControllerDelegate> delegate;
....
And then in the settingsTableViewController.m I synthesize the delegate and call the delegate method like so:
#synthesize delegate;
....
- (IBAction)logOut:(id)sender {
[self.delegate loggedOutUser:self];
....
}
Furthermore, in my responding menuTableViewController.h I set it up like so:
#interface MenuTableViewController : UITableViewController <SettingsTableViewControllerDelegate>
And then finally I set the delegate in the prepareForSegue method in my menuTableViewController.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
....
if ([[segue identifier] isEqualToString:#"settingsSegue"]) {
SettingsTableViewController *stvc = [segue destinationViewController];
stvc.delegate = self;
}
....
}
I simply cannot figure out what's causing this.
My guess is that the stvc.delegate = self doesn't do what it's supposed to, because the menuTableViewController responds to the delegate of another viewcontroller as well (not modal), and here I had to set the delegate = self in the viewDidLoad method, but before the [self viewDidLoad] call, as placing it after did not work.
Any help that could resolve this issue would be greatly appreciated! I've been struggling with this for too long now... Thanks!
Turns out my destinationViewController is actually a navigationController, so the delegate is not set correctly. Thanks to remus for telling me to set a breakpoint to check if the delegate was set correctly.
I fixed it by writing
SettingsTableViewController *stvc = ((UINavigationController *)[segue destinationViewController]).viewControllers[0];
instead.

Get ViewController from Storyboard

I have two ViewControllers, which aren`t (directly) connected with a Segue. But I want to change a String in the second VC, which i get in the first one. My try was:
#import "secondViewController.h"
#interface firstViewController ()
#property NSString* originalString;
#end
#implementation firstViewController
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc = [secondViewController new];
svc.anotherString = self.originalString;
}
But it dosent work, because I've only created a instance of the second VC, so the value was not saved. Also I can`t use the Storyboard ID, because I use Xcode 5.
I have a menuVC from which you can get to the firstVC and the secondVC. And from the firstVC I can go back (with the navigationbackbarbutton) to the menu. so: menu->firstVC->menu. menu->secondVC->...->menu
My try with StoryboardID:
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc =[[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"secondVCSrorybradID"];
svc.anotherString = self.originalString;
}
you can pass the string to second view controller with this code.
secondViewController* svc =[self.storyboard instantiateViewControllerWithIdentifier:#"Your Second VC's Storyboad ID"];
svc.anotherString = self.originalString;
[self presentViewController:svc animated:YES completion:nil];
//you have to create anotherString property in second View Controller's .h File.
now you can get the string of originalString to second VC. Now you can get this value back second VC to first VC.
hope this helps you.
You should pass your data successively through your UIViewControllers navigation. If, for example, you have a navigation like FirstVC > SecondVC > ThirdVC :
In your FirstVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((SecondVCClass*) segue.destinationViewController).secondVCString = _firstVCString;
}
With secondVCString being a #property in your second ViewController.
In your SecondVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((ThirdVCClass*) segue.destinationViewController).thirdVCString = _secondVCString;
}
With of course thirdVCString being a #property in your third ViewController.
Edit:
As you updated your question, here is what I suggest :
In your MenuVC, add this :
#property (nonatomic, weak) NSString *importantString;
In your FirstVC and SecondVC, add this :
#property (nonatomic, weak) MenuVCClass *menu;
When you push to FirstVC or SecondVC, use prepareForSegue to set the destination's view controller menu property to your menu :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"FirstSegue"])
((FirstVC*) segue.destinationViewController).menu = self;
else if ([segue.identifier isEqualToString:#"SecondSegue"])
((SecondVC*) segue.destinationViewController).menu = self;
}
In FirstVC or SecondVC, you can change the NSString value from your menu using _menu.importantString = #"";
You should never use new to create a view controller.
If you're using storyboards but not using segues, you can still create a view controller from the storyboard and invoke it.
Use the method instantiateViewControllerWithIdentifier: to create an instance of the target view controller. The set the properties you want to set, and finally make a call to display it (present it modally, push it onto the navigation stack, or whatever is appropriate for your program.)

Understanding the mechanism when passing data back from a second view controller to main view controller

I'm currently trying to have a better understanding on how the mechanisms of passing data between controllers work and I'm a little confused especially when passing data back from a second view controller to the main view controller.
This is what I have that works but don't fully understand. I have two view controllers, in the first one I have a button that when clicked it basically goes to the second view controller and a label which shows a message sent from the second view controller. In the second view controller I have a button and a textField, the button basically sends whatever is in the textfield to the label in main view controller.
Here is the code...
// FirstVC.h
#import <UIKit/UIKit.h>
#import "SecondVC.h"
#interface FirstVC : UIViewController <passNames>
#property (nonatomic, strong) NSString* firstNameString;
#property (weak, nonatomic) IBOutlet UILabel *firstNameLabel;
#end
//FirstVC.m
#import "FirstVC.h"
#implementation FirstVC
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"secondController"])
{
UINavigationController *navController = segue.destinationViewController;
SecondVC *vc2 = (SecondVC*)navController.topViewController;
[vc2 setDelegate:self];
}
}
-(void)viewWillAppear:(BOOL)animated
{
self.firstNameLabel.text = _firstNameString;
}
-(void)setFirstName:(NSString *)firstName
{
_firstNameString = firstName;
}
#end
//SecondVC.h
#import <UIKit/UIKit.h>
#protocol passNames <NSObject>
-(void)setFirstName:(NSString*)firstName;
#end
#interface SecondVC : UIViewController
#property (retain)id <passNames> delegate;
- (IBAction)send:(UIBarButtonItem *)sender;
#property (nonatomic, strong) NSString *firstNameString;
#property (weak, nonatomic) IBOutlet UITextField *firstNameText;
#end
//SecondVC.m
#import "SecondVC.h"
#import "FirstVC.h"
#interface SecondVC ()
#end
#implementation SecondVC
- (IBAction)send:(UIBarButtonItem *)sender
{
_firstNameString = _firstNameText.text;
[[self delegate]setFirstName:_firstNameString];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Can someone explain how the prepareForSegue method works in the above code? The reason for this question is because I added an NSLog and it looks like this method is only called in the transition from main view controller to the second controller. Why is this method needed if it is not called when transitioning from second view controller to main view controller which in my case is what I'm doing? It makes sense to use it when passing data from main view controller to a second controller not on the case shown above.
Can some explain the whole mechanism when passing data back to the main view controller?
FYI, I do understand about protocols and delegation.
Thanks a lot.
In your case, you are setting your delegate method of the second view controller to self in mainViewController in you prepareForSegue. This means that apart from navigating to the SecondViewController, you are implementing the callback mechanism in your main view controller, so that your delegate method gets called when the value is passed from the second view controller and this delegate method collects the value as a parameter to handle it in the main View Controller. You might have set the delegate of VC2 as self inn your prepareForSegue because you are creating the instance of VC2 in this method to navigate to the second controller.
Your goal is to hand back the data, like this:
[[self delegate] setFirstName:_firstNameString];
But you can't do that unless you know who to send setFirstName: to, and the compiler won't let you do it unless you guarantee that whoever you are sending setFirstName: to can accept that message.
That is what prepareForSegue prepares. FirstVC has declared that it adopts the passNames protocol, which means that it implements setFirstName:. And now you are saying:
[vc2 setDelegate:self];
...where self is the FirstVC instance. This solves both problems at once. The SecondVC instance (vc2) now has a delegate (the FirstVC instance), it is the right object to send the info back to, and because its delegate is declared as adopting passNames, we know that SecondVC can actually send setFirstName: to that delegate.
Now to the heart of your actual question: The reason for doing this in prepareForSegue is merely that this is the only moment when the FirstVC instance and the SecondVC instance "meet" one another! There is no other moment when the FirstVC instance has a reference to the SecondVC instance so as to be able to call setDelegate on it in the first place. If you weren't using segues and storyboards, the FirstVC would simply create the SecondVC instance directly - and would set itself as its delegate, just as you do:
SecondVC *vc2 = [SecondVC new];
UINavigationController *nav = [
[UINavigationController alloc] initWithRootViewController: vc2];
[vc2 setDelegate:self];
[self presentViewController: nav animated: YES completion: nil];
This is one reason I don't like storyboards: they muddy the story. It's all so simple and obvious when you don't use them and just do everything directly like this.

How to add subview to another viewcontroller?

I'm doing an Ipad app. Now I have 2 viewcontrollers, ViewController has a button1 which has a popover segue to the second viewcontroller(PopoverController). Then, the PopoverController has a button2, if I click the button2, I'll receive some UIImage from my server. I want to add fews subviews of UIImageView to the ViewController to display these images if I click the button2.
The button1 works well, the PopoverController can pop up as expected. BUT when I click the button2, nothing happend. I want to know how can I pass the data between 2 viewcontrollers and how to add subviews to another one.
Some codes relating to my problem:
ViewController.h:
#import <UIKit/UIKit.h>
#class PopoverController;
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button1;
#property (strong, nonatomic) PopoverController *popoverController;
#end
PopoverController.h:
#import <UIKit/UIKit.h>
#class ViewController;
#interface PopoverController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button2;
#property (strong, nonatomic) UIImage *tempImg;
#property (strong, nonatomic) ViewController *viewController;
- (IBAction)addsubviews:(id)sender;
#end
I can not just use [viewController.view addSubview:img1]; in the - (IBAction)addsubviews:(id)sender;method to addsubview. So someone can help me? :)
====1st update====
Someone suggest that I have to use - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method. I have tried this one by control click the button2 and create a custom segue between button2 and ViewController. When I clicked the button2, it showed :
Terminating app due to uncaught exception 'NSGenericException', reason: 'Could not find a navigation controller for segue 'change'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
So I'm wondering whether I should add a NavigationController. If so, what should I do?
====2nd update====
I use Paramasivan 's code now, and I found the way to call method from another viewcontroller. The problem now is the newly added subview in my viewcontroller doesn't show up. I guess I have to update my viewcontroller in order to make it visible.
in my - (IBAction)addsubviews:(id)sender; method, i invoke the method in ViewController by [self.viewController createSubViewWithImage:_tempImg];
so the method can be invoked when i click the button2, but the view of viewcontroller has nothing changed.
Add this in - (void)viewDidLoad,
self.popoverController = [[PopoverController alloc] init];
self.popoverController.viewController = self;
Make sure that in no other places, you are setting self.popoverController = ....
Do NOT add anything like self.viewController = ... in popovercontroller class. And you dont have to do self.viewController.popoverController = self; as well. Just remove these lines if you already have it.
Once these are done, make sure that you are displaying self.popoverController only in the popover and you are not creating a new object for popoverController class there. So if these are fine, you can use any approach you want for passing the image from popoverController class to viewController class.
as you mentioned in your comment you can use [self.viewController createSubViewWithImage:_tempImg]; in your popovercontroller class.
Update:
If you are doing via storyboard, you need to set this in prepareForSegue method and you dont have to create self.popoverController at all. Remove that part in your case. You can follow the procedure mentioned here to set up a custom segue and implement prepareForSegue method to pass the object. Source: On storyboards, views and passing data along
Set the name of segue in storyboard to "CustomSegue"
Implement prepareForSegue method
Inside the method, check if name of segue matches "CustomSegue" and then set the viewController in the popoverController object there as,
Try,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"CustomSegue"]) {
PopoverController *popoverController = [segue destinationViewController];
popoverController.viewController = self;
}
}
After doing this, you need to call [self.viewController createSubViewWithImage:_tempImg]; in your popoverController class.
Check out the Communicating with Objects doc, there are several ways to do what you want.
In ViewController.h add the following
-(void)createSubViewWithImage:(UIImage *)imageDownloaded {
UIImageView *imageViewTemp = [[UIImageView alloc] initWithImage:imageDownloaded];
[self.view addSubView:imageViewTemp];
}
In PopoverController.h add the following
#property (nonatomic, retain) ViewController *viewControllerPassed;
And after image downloaded, call the following in PopoverController.h
[viewControllerPassed createSubViewWithImage:imageDownloaded];
You can pass data using the below method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ViewController2"])
{
ViewController2 *v2VC = [segue destinationViewController];
v2VC.yourData = self.someStuff;
}
}

Resources