Identifying an incoming segue - objective-c

So, I was reading this related SO, as it ended up being something I wanted to do as well.
I added a property to my destinationViewController's header file:
#property (nonatomic, strong) NSString *incomingSegue;
And I've got it synthesized in the destinationViewController's implementation file:
#synthesize incomingSegue = _incomingSegue;
I've added the following lines to my prepareForSegue method, for the sourceViewController (depending on which is triggering the segue):
[segue.destinationViewController setIncomingSegue:#"edit"];
[segue.destinationViewController setIncomingSegue:#"add"];
And, finally, I've got a process to check for which value is set in my destinationViewController's implementation file:
if (_incomingSegue == #"add")
{
//snipped code here
}
else if (_incomingSegue == #"edit")
{
//snipped code here
}
So, apparently I am missing something. When I try to perform the segue I get an error that shows up about 1000 times in SO, which makes it rather difficult to figure out which detail I've overlooked. This thing triggers (according to breakpoints) in my prepareForSegue method on my sourceViewController:
unrecognized selector sent to instance
Can I not use a literal string (#"string") in place of a (NSString *), or is it something else throwing the error?
Update (solved):
More detailed description of my prepareForSegue method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"editSegue"])
{
//snipped
[segue.destinationViewController setIncomingSegue:#"edit"];
}
else if ([[segue identifier] isEqualToString:#"addSegue"])
{
//snipped
DestinationViewController *dtv = (DestinationViewController *)[[segue destinationViewController]topViewController];
[dvc setIncomingSegue:#"add"]
}
}
Turns out, I had to use my declared DestinationViewController class object to set the value. Instead of just referencing the segue.destinationViewController as I did for the editSegue. I do not have a DestinationViewController class object declared for editSegue, so that one was/is working as intended.

You should add a condition around the line that sets the incoming segue:
if ([segue.identifier isEqualToString:#"SegueToControllerThatSupportsIncomingSegue"]) {
[segue.destinationViewController setIncomingSegue:#"edit"];
}
The idea is to call setIncomingSegue: only on the destination view controller that supports your added method.
You should also change the code in the destination view controller to check string equality with isEqualToString:
if ([_incomingSegue isEqualToString:#"add"])
{
//snipped code here
}
else if ([_incomingSegue isEqualToString:#"edit"])
{
//snipped code here
}

When you call
#synthesize incomingSegue = _incomingSegue;
You really only need to call
#synthesize incomingSegue;

Related

Method does not get run

I am trying to run a non-void function returning an NSArray, but when I run it, there's not even a log line:
- (NSArray *) arrayFunction
{
return myList;
}
This is how I call the function:
- (void) myMainFunction
{
[self arrayFunction];
}
I also tried with NSLog and a void function instead of NSArray, but that won't show up either.
It is a NSView class.
Thanks for you help!
*EDIT: * Full Code:
Implementation file:
#import "LogNavigator.h"
#implementation LogNavigator
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
[self myMainFunction];
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
}
- (NSArray *) arrayFunction
{
// # Get the list of .txt files, this part works correctly as expected in CodeRunner
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"Desktop"];
NSArray *directoryList = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil]
pathsMatchingExtensions:[NSArray arrayWithObjects:#"txt", nil]];
return directoryList;
}
- (void) myMainFunction
{
[self arrayFunction];
}
#end
If this custom NSView is created using Interface Builder then you should override awakeFromNib:
- (void)awakeFromNib {
[self myMainFunction];
}
Note, however, that you are ignoring the return from arrayFunction, so it's feasible the compiler might omit the call entirely during an optmimized release build if it can determine no side effects of the call.
EDIT: Note that you need to set the NSView-derived class within the view of the window within MainMenu.xib, using IB, in order for this method to be triggered when the view is loaded.
When objects are loaded from a nib (or a Storyboard) The normal initialiser is not called, instead initWithCoder is called.
When setting up objects from a nib, you should override initWithCoder and put your initialiser code in there instead.
If you need to access or set up other objects in the nib as part of initialisation, then awakeFromNib is a better method to override, because it is called after all the other nib objects have been loaded.
edit
When you added the view object to the nib, did you specify it's class as LogNavigator? This is something that people frequently forget to do.

Objective-C iOS 6 delegate NSString query

I'm trying to use a delegate to pass a value from one VC to another. I think I'm am misunderstanding the way it is supposed to work.
In my main ViewController.h I have this:
#protocol defaultLocationChoice <NSObject>
- (NSString *) locChoice;
#end
In both my PreferencesViewController.h and ChooseServerViewController.h I have defaultLocationChoice declared in the #interface section and the property assinged like so:
#property (nonatomic, assign) id <defaultLocationChoice> locationDelegate;
Both are synthesized also.
When the user segues from PreferencesViewController to ChooseServerViewController the prepare for segue code is:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toServerChoice"]) {
ChooseServerViewController *viewController = (ChooseServerViewController *)segue.destinationViewController;
viewController.locationDelegate = self;
}
}
When a cell choice is made in ChooseServerViewController I call:
[self locChoice];
Which is:
- (NSString *) locChoice {
NSLog(#"Cell Vale Loc choice %#",cellValue);
return cellValue;
}
The NSLog verifies the correct value is returned.
Now, as I think I understand it, the value of LocChoice in the delegate is now the value returned, no?
When the user goes back (NavController) the PreferencesViewController has:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
defaultLocation = [locationDelegate locChoice];
[self.tableView reloadData];
}
I was expecting the value of defaultLocation to now equal the value passed to locChoice. However when the table reloads the cell in question is still blank, implying what I exepct to happen isn't happening.
Any ideas?
If I followed your code properly, you do not need to adopt the mentioned protocol in your ChooseServerViewController, only PreferencesViewController.
The reasoning is you want to send data back to the previous view controller. Try:
#protocol defaultLocationChoice <NSObject>
- (void) locChoice:(NSString*)choice;
#end
Have your PreferencesViewController implement that method so it receives the selection. You will have to store that in an appropriate instance variable.
// in PreferencesViewController.m
-(void)locChoice:(NSString*)choice {
self.choice = choice; // this just my example
}
When the choice is made (in ChooseServerViewController) to send the choice back, call:
// this is in 'ChooseServerViewController.m' some where appropriate
[self.delegate locChoice:cellValue];
Your implementation is simply doing nothing with cell value (not even storing it, just logging it). When you return to PreferencesViewController, you will now have the selected value and that view controller can what it wants with it.
Protocols are somewhat analgous to interfaces in Java or C#, but more flexible.
Some more concepts about delegation.
Working with Protocols.
UPDATE:
The declaration for ChooseServerViewController should look like:
#import "FileWithProtocolDecalration.h"
#interface ChooseServerViewController
.
.
.
#property ( nonatomic,assign) id<defaultLocationChoice> delegate;
.
.
.
#end
I think you do have some misunderstanding there
protocol and delegates are something that is passed around. in other words somebody need to be receiver and somebody need to be the sender. in your case.
update your protocol to this
#protocol defaultLocationChoice <NSObject>
- (void)locChoice:(NSString *)updateString; // the method from delegate and implementer must be exact
#end
and set implement the protocol to ViewController as a receiver
#interface VCReceiver : UIViewController <defaultLocationChoice>
then later in VCReceiver
- (void)viewDidLoad {
ChooseServerViewController *vcSender = [[ChooseServerViewController alloc] init];
[vcSender setLocationDelegate:self]; // this is like saying. i have implemented method from protocol in this file (self)
}
- (void)locChoice:(NSString *)updateString {
// update the VCReceiver here
// or access vcSender value
// or use the updateString value
}
then in ChooseServerViewController locChoice: method (the one from your example) replace with this one and call [self updateChoice] instead:
- (void)updateChoice {
if ([self.locationDelegate respondsToSelector:#selector(locChoice:)]) {
[self.locationDelegate locChoice:aStringToUpdate]; // this will call VCReceiver locChoice
}
it does not have to return anything because it is actually calling the VCReceiver method to tell it that ChooseServerViewController got the value ready to be read.

Transferring Data from One View to Another

I have one string in one view controller and trying to transfer its value that it has to another string in another view controller. Here is my code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Check Mark Segue"])
{
NSLog(#"Transferring Data");
RenewDrop *controller = segue.destinationViewController;;
controller.transferData = self.renewString;
}
}
I thought this would work because it seems rather easy. However, Whenever I NSLog the transferData string in the next view it says that it is (null).
What am I doing wrong here?
EDIT:
#RenewDrop.h
#interface RenewDrop : UITableViewController <UITableViewDelegate> {
NSString *transferData;
}
#property (nonatomic, retain) NSString *transferData;
#RenewDrop.m
#implementation AutoRenewDrop
#synthesize transferData;
Okay so here is what happened: I had code in my viewDidLoad on Renew Drop that I didnt see:
//AddReminder *controller = [[AddReminder alloc] init];
//self.transferData = controller.renewString;
Once I did this everything worked just like above.
Thanks to everyone for the help!

Undeclared method in Objective-C

#import "PsychologistViewController.h"
#import "HappinessViewController.h"
#interface PsychologistViewController()
#property (nonatomic) int diagnosis;
#end
#implementation PsychologistViewController
#synthesize diagnosis = _diagnosis;
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowDiagnosis"]) {
[segue.destinationViewController setHappiness:self.diagnosis];
}
else if ([segue.identifier isEqualToString:#"Celebrity"]) {
[segue.destinationViewController setHappiness:100];
}
else if ([segue.identifier isEqualToString:#"Serious"]) {
[segue.destinationViewController setHappiness:20];
}
else if ([segue.identifier isEqualToString:#"TV Kook"]) {
[segue.destinationViewController setHappiness:50];
}
}
****- (void)setAndShowDiagnosis:(int)diagnosis****
{
self.diagnosis = diagnosis;
[self performSegueWithIdentifier:#"ShowDiagnosis" sender:self];
}
-(IBAction)flying
{
[self setAndShowDiagnosis:85];
}
-(IBAction)apple
{
[self setAndShowDiagnosis:100];
}
-(IBAction)dragons
{
[self setAndShowDiagnosis:20];
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
My question pertains to the - (void)setAndShowDiagnosis:(int)diagnosis method. This method is undeclared anywhere as either public in any .h file and obviously it's not there privately either. My question is why the reason for this would be? It just shows its setter implementation but the actual method declaration appears nowhere. Any help to clarify this is appreciated. Oh and this is from an online lecture and everything compiles just fine and runs.
Methods do not need to be declared, publicly, privately, or otherwise. Declaring a method in a .h file gives other users of the class knowledge of those methods. By not declaring it, you are hiding that method from the rest of the program that is using the class.
This method is undeclared anywhere as either public in any .h file and
obviously it's not there privately either.
I think you answered your own question, its coming up undeclared, because it isn't being declared. Unless I am reading this wrong?
You don't have to declare methods if you want them to private. There is no such thing as private methods in objective-c.
The difference between declaring a method in the header file, and a class extension at the top of implmentation file, is that if you don't declare it in the header, and you use the method from another class then the compiler will warn you that the method may not exist. But as long as you've implemented the method the application will not crash and the method will be called.
You could get away with not declaring any methods anywhere, but you will get lots of compiler warnings and it is harder to read later on, and harder for other people to understand your code. And there will be greater chance of you causing a crash because you miss spelt a method name,or some other trivial mistake.

Distinguish between two instances of the same class Objective-c?

I have two UIWebView objects that share the same delegate.
I want the delegate methods to distinguish between the two UIWebView objects.
Right now I'm setting the background color of one of them to red and using that property to distinguish between the two, but its messy. There must be a better way...
Thanks!
I'd keep a pointer to each UIWebView instance in the delegate and just compare them in the delegate methods. e.g.,
#property (nonatomic, weak) UIWebView* webView1;
#property (nonatomic, weak) UIWebView* webView2;
...
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (self.webView1 == webView) {
// do something specific to webView1
} else if (self.webView2 == webView) {
// do something specific to webView2
}
}
Delegate methods in general have the instance that's calling them as one of the arguments -- e.g. in webView:didFailLoadWithError: the first argument will be the web view which has failed to load.
A delegate/controller object will/should already have references to the instances for which it's delegating.
All you have to do, then, is compare the reference you already have to the object that's passed along in the delegate method to know which web view has messaged the delegate.
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
if( webView == myFirstWebView ){
}
else if( webView == mySecondWebView ){
}
}
all UIView subivews, including UIWebView has a tag property, that can be used as following:
const NSInteger kLeftWebView = 20;
const NSInteger kRightWebView = 21;
...
// somewhere in initialization code
webView1.tag = kLeftWebView;
webView2.tag = kRightWebView;
...
//check for tag value for distinguishing
if (webView.tag == kLeftWebView) {
....
}
you can also setup tag value in InterfaceBuilder property inspector.