I have the following code, and have been struggling with it for quite some time. I have two view controllers, FirstView and SecondView. I pushViewController from FirstView to SecondView. In SecondView there is a UITextView where I take user's input. Then I save that input using a delegate in SecondView to FirstView's variable called text. When I run this, the code gets into an infinite loop on calling the delegate from SecondView.
FirstView.m
UIStoryboard *story=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
SecondView *secondView = [story instantiateViewControllerWithIdentifier:#"SecondView"];
secondView.delegate = self;
[self.navigationController pushViewController:secondView animated:YES];
-(void)setText:(NSString *)strData
{
NSLog(#"Entered setText delegate");
NSLog(#"Current string is %#", strData);
self.text = strData;
}
SecondView.h
#protocol SetInstructionDelegate <NSObject>
-(void)setText:(NSString *)strData;
#end
#property (weak, nonatomic) IBOutlet UITextView *textView;
#property (nonatomic, weak) id<SetInstructionDelegate> delegate;
SecondView.m
-(void)viewDidLoad {
/****************************** Done Button framing ********************************/
UIButton *btn_bar=[[UIButton alloc]initWithFrame:CGRectMake(0, 0, 60, 60)];
[btn_bar setBackgroundColor:[UIColor clearColor]];
[btn_bar setTitle:#"Done" forState:UIControlStateNormal];
[btn_bar addTarget:self action:#selector(doneEditing:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *doneEdit=[[UIBarButtonItem alloc]initWithCustomView:btn_bar];
self.navigationItem.rightBarButtonItem=doneEdit;
}
-(void)doneEditing:(id) sender
{
[self.view.window endEditing: YES];
[self.navigationController popViewControllerAnimated:YES];
NSLog(#"Current text is : %#", self.textView.text);
[[self delegate] setText:self.textView.text];
}
The code stays in doneEditing, and keeps printing the NSLogs repeatedly. I read quite a few links on this but cannot find a definite answer and have been struggling. I am very new to iOS and delegate methods. Any help would be appreciated. Thanks!
The problem is that you've got a property called text and a method called setText:. Give that method (or that property, I don't care which) a different name. Really it should be the method; giving a method wantonly a name starting with set... is always potentially a very dangerous thing to do.
The reason is that setting a property called text is in fact nothing but a shorthand for calling a method called setText:. Thus, this is an infinite recursion:
-(void)setText:(NSString *)strData
{
self.text = strData;
}
That code, you see, is exactly the same as saying:
-(void)setText:(NSString *)strData
{
[self setText: strData];
}
Behold your infinite recursion in all its glory.
Related
I have a textfield on my MainViewController that I'd like to pass a string into from my TableViewController. Specifically when I select a cell (didSelectRowatIndexPath) I'd like to take the text for that indexpath.row and dismiss the TableViewController passing the string into the textfield on my MainViewController. I have attempted to create a delegate to get this to work but all it says in the debugging window is that the correct string is passing but never appears in the textfield... Here is my code showing everything necessary for the delegation.
My TableViewController.h where the delegate is declared...
#protocol sendDataProtocol <NSObject>
- (void)sendDataToMain:(NSString*)text;
#end
#interface TableViewController : UITableViewController<UITableViewDelegate, UITableViewDataSource> {
__weak id selectDataDelegate;
}
#property(nonatomic,weak)id<sendDataProtocol> selectedDataDelegate;
#property(strong,nonatomic)NSArray *presetList; //Holds the strings I want to pass
#end
Then my TableViewController.m file...
#interface TableViewController ()
#end
#implementation TableViewController
#synthesize selectedDataDelegate;
-(void)viewDidLoad {
//http://morsecode.scphillips.com/morse.html
self.presetList = [NSArray arrayWithObjects:#"AS",
#"BCNU",
#"CL",
#"CT",
#"CUL",
#"K",
#"QSL",
#"QSL?",
#"QRX?",
#"QRV",
#"QRV?",
#"QTH",
#"QTH?",
#"R",
#"SN",
#"SOS",
#"73",
#"88",
nil];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.selectedDataDelegate sendDataToMain:self.presetList[indexPath.row]];
NSLog(#"Delegate says: %#", self.presetList[indexPath.row]);
//The NSLog does display the correct cell I pressed, but no data passes back
[self dismissViewControllerAnimated:YES completion:nil];
}
Now here is my MainViewController.h file, this is where my textfield resides, and how I implement the delegate into this file...
#interface MainViewController : UIViewController<UITextFieldDelegate, CAAnimationDelegate, sendDataProtocol> //include protocol here
#property(strong,nonatomic)UITextField *morseTextfield;
- (void)sendDataToMain:(NSString*)text; //conform to protocol
#end
Now the MainViewController.m file...
- (void)viewDidLoad {
[super viewDidLoad];
TableViewController *tvc = [TableViewController new];
tvc.selectedDataDelegate = self;
}
//Protocol method declared here
- (void)sendDataToMain:(NSString*)text {
NSString *str = text;
self.morseTextfield.text = str;
NSLog(#"text: %#",text);
}
The textField NSLog never displays anything, so its not connecting to the delegate or something.
So something is clearly wrong but I'm not sure what. I used this stackoverflow answer as a reference but even then couldn't get it to work (refer to the passing data back section)
Passing Data between View Controllers
Also as a side note I'm coding everything programmatically. Any help is appreciated, thank you.
This is how i created the textfield...
//CONFORMING TO DELEGATES
self.morseTextfield.delegate = self;
//CREATING AND ADDING TEXTFIELD TO VIEW
self.morseTextfield = [[UITextField alloc]initWithFrame:CGRectMake((self.view.frame.size.width-300)/2,
(self.view.frame.size.height)/7, 300, 30.0)];
self.morseTextfield.borderStyle = UITextBorderStyleRoundedRect;
self.morseTextfield.font = [UIFont fontWithName:#"Avenir Next" size:20];
self.morseTextfield.textAlignment = NSTextAlignmentCenter;
self.morseTextfield.placeholder = #"Translate text into morse code";
[self.morseTextfield addTarget:self action:#selector(dismissKeyboard) forControlEvents:UIControlEventEditingDidEndOnExit];
self.morseTextfield.autocorrectionType = UITextAutocorrectionTypeNo;
self.morseTextfield.spellCheckingType = UITextSpellCheckingTypeNo;
self.morseTextfield.autocapitalizationType = UITextAutocapitalizationTypeNone;
[self.morseTextfield setReturnKeyType:UIReturnKeyDone];
[self.view addSubview:self.morseTextfield];
Possibly you set delegate to one instance of TableViewController and display another one.
- (void)viewDidLoad {
[super viewDidLoad];
TableViewController *tvc = [TableViewController new];
tvc.selectedDataDelegate = self;
}
in your code tvc will be just released from memory and you delegate will not work.
Also in you .h file this row is useless.
- (void)sendDataToMain:(NSString*)text; //conform to protocol
In your MainViewController update next method. You have to set delegate in it
- (void) tableViewBtnPressed:(UIBarButtonItem *)sender {
MCTableViewController *tableVC = [[MCTableViewController alloc] init];
tableVC.selectedDataDelegate = self;
UINavigationController *navBar = [[UINavigationController alloc]initWithRootViewController:tableVC];
[self.navigationController presentViewController:navBar animated:YES completion:nil];
}
I have a UIViewController with a single button and an activity indicator.
In the class for this VC MainViewController.m I do the following in viewDidAppear:
- (void)viewDidLoad
{
[super viewDidLoad];
_actLoadLoc.color = [UIColor blueColor];
_startButton.enabled = NO;
[_startButton setTitle:#"Fetching Location" forState:UIControlStateDisabled];
}
Another method in my MainViewController.m is called readyToGo and is implemented as follows:
-(void) readyToGo
{
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"Done sleeping");
_startButton.enabled = YES;
[_startButton setTitle:#"Start" forState:UIControlStateNormal];
_actLoadLoc.stopAnimating;
}
I have properties for both UIButton, UIActivityIndicatorView and a declaration of the readyToGo method in my MainViewController.h as follows:
#property (weak, nonatomic) IBOutlet UIButton *startButton;
#property (weak, nonatomic) IBOutlet UIActivityIndicatorView *actLoadLoc;
-(void) readyToGo;
The readyToGo method is called from another class abc.[h/m] which imports MainViewController.h. The call happens after one of the functions in abc.m completes filling an array with calculated data.
The call works since Done Sleeping shows in the output, however the startButton is not enabled, its test does not change and the actLoadLoc does not stop animating... Any idea what's wrong with my code/method?
Thanks in Advance!
You are calling the readyToGo on the wrong instance of the view controller. You have an instance which is displaying content on the screen and you are, in some way, creating a new one to call the method on. You need to get the existing one instead.
It's not ideal, but you should be able to get the controller with:
UINavigationController *n = (UINavigationController *)[UIApplication sharedApplication].keyWindow.rootViewController;
SDPPMainViewController *mvc = (SDPPMainViewController *)[n viewControllers][0];
(Will need to add some casts, and should probably break out to multiple lines)
I just want to copy UITableViewCell's label (which is a simple string) into nextView's UIlabel.
I tried creating a string property in the nextView and passing it the cell label,
but it doesn't work.
I'm getting nil in nextView,
why is that? Here is my didSelectRowAtIndexPath Method
in rootViewController.m
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selLabel =[tempArray objectAtIndex:indexPath.row];
DetailViewController *detailViewCont=[[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
detailViewCont.selectedLabel=selLabel;
[self.navigationController pushViewController:detailViewCont animated:YES];
NSLog(#"selected Label %#",detailViewCont.selectedLabel);
}
Last NSlog statement returns the correct string here.
In nextViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
selectedLabel=[[NSString alloc]init];
UILabel *label1=[[UILabel alloc]init];
label1.frame=CGRectMake(5,5,310, 60);
label1.font=[UIFont fontWithName:#"Arial Black" size:20];
label1.text=selectedLabel;
NSLog(#"sellabel %#",selectedLabel);
[self.View addSubview:label1];
}
NSLog statement here returns null
Delete the following line from you viewDidLoad method:
selectedLabel=[[NSString alloc]init];
This is clearing the value you set from the other view controller.
And this line:
label1.text=selectedLabel;
should really be:
label1.text = self.selectedLabel;
You setup a property, use it.
Try setting the selLabel before the pushViewController statement like below,
detailViewCont.selectedLabel=selLabel;
[self.navigationController pushViewController:detailViewCont animated:YES];
Move your line detailViewCont.selectedLabel=selLabel; before the pushViewController call. Your presenting the viewcontroller before you assign the value.
#interface DetailViewController
...
#property (strong, nonatomic) NSString *selectedLabel;
#end
#implementation DetailViewController
#synthesize selectedLabel = _selectedLabel; // define the instance variable associated with the property
-(void)viewDidLoad {
label1.text = _selectedLabel;
}
#end
I'm trying to use a Button in my UIPopover to create a UITextView in my Main UIViewController the code I have looks something like this (PopoverView.h file):
#protocol PopoverDelegate <NSObject>
- (void)buttonAPressed;
#end
#interface PopoverView : UIViewController <UITextViewDelegate> { //<UITextViewDelegate>
id <PopoverDelegate> delegate;
BOOL sendDelegateMessages;
}
#property (nonatomic, retain) id delegate;
#property (nonatomic) BOOL sendDelegateMessages;
#end
Then in my PopoverView.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton * addTB1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
addTB1.frame = CGRectMake(0, 0, 100, 50);
[addTB1 setTitle:#"Textbox One" forState:UIControlStateNormal];
[self.view addSubview:addTB1]; // Do any additional setup after loading the view from its nib.
[addTB1 addTarget:self action:#selector(buttonAPressed)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)buttonAPressed
{
NSLog(#"tapped button one");
if (sendDelegateMessages)
[delegate buttonAPressed];
}
And also in my MainViewController.m :
- (void)buttonAPressed {
NSLog(#"Button Pressed");
UITextView *textfield = [[UITextView alloc] init];
textfield.frame = CGRectMake(50, 30, 100, 100);
textfield.backgroundColor = [UIColor blueColor];
[self.view addSubview:textfield];
}
I'm using a delegate protocol to link the popover and the ViewController but I'm stuck on how I get my BOOL statement to link the -(void)buttonAPressed in the PopoverView and MainViewController so that when I press the button in the Popover a textview appears in the Main VC. How would I go about doing this?
In MainViewController, where you create PopoverView, be sure to set its delegate property otherwise sending messages to delegate in PopoverView will do nothing.
For example, in MainViewController.m:
PopoverView *pov = [[PopoverView alloc] initWithNibName:nil bundle:nil];
pov.delegate = self; // <-- must set this
thePopoverController = [[UIPopoverController alloc] initWithContent...
I am not sure why you need the sendDelegateMessages variable. Even with that bool, you must set the delegate property so PopoverView has an actual object reference to send the messages to.
If you want to make sure the delegate object has implemented the method you're about to call, you can do this instead:
if ([delegate respondsToSelector:#selector(buttonAPressed)])
[delegate buttonAPressed];
Also, the delegate property should be declared using assign (or weak if using ARC) instead of retain (see Why use weak pointer for delegation? for an explanation):
#property (nonatomic, assign) id<PopoverDelegate> delegate;
Another thing is if you're not using ARC, you need to add [textfield release]; at the end of the buttonAPressed method in MainViewController to avoid a memory leak.
I received this message from the debugger console :
-[UIButton release]: message sent to deallocated instance 0x1836b0
But I don't create UIButton programmatically, all my buttons have created in Interface Builder. Each of them are linked to a function, like this :
-(IBAction)theFunction:(UIButton *)sender;
In lot of this functions, I don't use the variable sender. I don't even try to release it. So I don't understand why my application try to release my buttons.
Do I do something in Interface Builder to release or not my UIButton? Is it about the picture I put in the UIButton? If I use the variable (UIButton *)sender, do I need to release it?
This problem stucks me because of it my application crashes.
Edit:
- (IBAction)showPopoverOverview:(UIButton *)sender {
TouchPlanePopover *content = [[TouchPlanePopover alloc] init];
[content setTheAlbum:#"Overview"];
// Setup the popover for use in the detail view.
detailViewPopover = [[UIPopoverController alloc] initWithContentViewController:content];
detailViewPopover.popoverContentSize = CGSizeMake(600., 400.);
detailViewPopover.delegate = self; // Set the sender to a UIButton.
// Present the popover from the button that was tapped in the detail view.
[detailViewPopover presentPopoverFromRect:sender.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
// Set the last button tapped to the current button that was tapped.
lastTappedButton = sender;
[content release];
}
detailViewPopover is create in .h like this : #property (nonatomic, retain) UIPopoverController *detailViewPopover;
Thans for your help
Let me know if you need more information, I will edit the post
Change
-(IBAction)theFunction:(UIButton *)sender
to
-(IBAction)theFunction:(id)sender
Just try...
I am working on this and its work properly
So may be its useful for you.
myController.h
#import <\UIKit/UIKit.h>
#class myController;
#interface myController : UIViewController {
}
- (IBAction)onButtonClick:(UIButton *)button;
#end
myController.m
#implementation myController
- (IBAction)onButtonClick:(UIButton *)button {
NSLog(#"work properly");
}