So I'm trying to get a hang of using delegates, and I've watched a few tutorials on how to use them so far. I still find them confusing and after trying to implement one myself, have an issue that I can't seem to solve.
I have two ViewControllers, the first one ViewController contains a UITextField *sampleTextField and a button with the method switchViews. It also contains the protocol declaration with the method sendTextToViewController. SwitchViews is also linked to a segue that switches to the SecondViewController. In SecondViewController the only object is a UILabel *outputLabel When the user taps the button, it calls switchViews and the view changes to SecondViewController, and upon loading outputLabel should be changed to whatever text was entered in sampleTextField in ViewController. However the delegate method sendTextToViewController is never being called. All objects are created in Interface Builder.
Here is the code to make it a bit easier to understand:
ViewController.h
#import <UIKit/UIKit.h>
#protocol TextDelegate <NSObject>
-(void)sendTextToViewController:(NSString *)stringText;
#end
#interface ViewController : UIViewController
- (IBAction)switchViews:(id)sender;
#property (weak, nonatomic) IBOutlet UITextField *sampleTextField;
#property (weak, nonatomic) id<TextDelegate>delegate;
#end
Then declared this in ViewController.m
- (IBAction)switchViews:(id)sender {
NSLog(#"%#", self.sampleTextField.text);
[self.delegate sendTextToViewController:self.sampleTextField.text];
}
SecondViewController.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface SecondViewController : UIViewController <TextDelegate>
#property (weak, nonatomic) IBOutlet UILabel *outputLabel;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize outputLabel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
ViewController *vc = [[ViewController alloc]init];
[vc setDelegate:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)sendTextToViewController:(NSString *)stringText
{
NSLog(#"Sent text to vc");
[outputLabel setText:stringText];
}
I've looked at this and the first answer makes sense, but for some reason it's not working.
I do think that the problem is where I am setting calling [vc setDelegate:self], but not sure how to fix this. Some pointers in the right direction would be greatly appreciated. Keep in mind I'm new to obj-c so if you can explain what you are saying, that would be great. Thank you.
Your are creating a new instance of ViewController but you don't do anything with it.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
ViewController *vc = [[ViewController alloc]init];
[vc setDelegate:self];
}
The SecondViewController needs to have reference to the FirstViewController to be able to set itself as a delegate.
First you don't have to use delegation to do such a program.
A simpler way would be just creating a property in the SecondViewController that you'll pass the content of the textField into it.
Your code doesn't work because you called sendTextToViewController on a delegate that hasn't been set. You have set the delegate to a new instance of ViewController, not the one presented onscreen.
Related
I have two view controllers. I want to make a thing like that when i press "Back" button in SecondViewController, it go back to "FirstViewController" and also update the text of label of "FirstViewController".
I have tried below code, its working fine that when i press back button it goes back to "FirstViewController"but the issue is that its not updating the text of the label of "FirstViewController".
When i debug my code, control goes to "startSampleProcess" method and update the label's text but when we go back to "FirstViewController" by "processCompleted" method,the old text is displaying there.
(I'm doing this work using delegates)
Any help would be appreciated.
FirstViewController.h
#import <UIKit/UIKit.h>
//#import "SampleProtocol.h"
#protocol SampleProtocolDelegate <NSObject>
#required
- (void) processCompleted;
#end
// Protocol Definition ends here
#interface FirstViewController : UIViewController{
id <SampleProtocolDelegate> _delegate;
IBOutlet UILabel *myLabel;
}
#property (nonatomic,strong) id delegate;
-(void)startSampleProcess; // Instance method
#end
FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize delegate;
-(void)startSampleProcess{
myLabel.text = #"we are back!!!";
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self.delegate
selector:#selector(processCompleted) userInfo:nil repeats:NO];
}
- (void)viewDidLoad{
[super viewDidLoad];
}
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface SecondViewController : UIViewController<SampleProtocolDelegate>{
FirstViewController *sampleProtocol;
}
-(IBAction)CallBack:(id)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#implementation SecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad{
[super viewDidLoad];
}
-(IBAction)CallBack:(id)sender{
sampleProtocol = [[FirstViewController alloc]init];
sampleProtocol.delegate = self;
[sampleProtocol startSampleProcess];
}
#pragma mark - Sample protocol delegate
-(void)processCompleted{
[self.navigationController popViewControllerAnimated:TRUE];
}
#end
It looks like the main problem is in your CallBack: method. It is creating a new instance of FirstViewController rather than referencing the existing view controller.
But even then, this feels like the wrong way to do this, as Earl mentioned in the comments.
Normally, you shouldn't need one view controller to reference another. That will result in tight coupling (bad). An easy way would be to use NSNotificationCenter, but there are lots of options here.
There are many ways that you can update the state of a view controller when a different view controller does something. See my answer on a similar question this week: What is the best way to keep the state of a modal UIViewController?
I am trying to follow these instructions but now I am stuck.
In your view controller:
Add an outlet for the TKCalendarMonthView.
#interface YourViewController () <TKCalendarMonthViewDataSource, TKCalendarMonthViewDelegate>
#property (weak, nonatomic) IBOutlet TKCalendarMonthView *calendarMonthView;
#end
In -viewDidLoad, connect TKCalendarMonthView's delegate and data source. Note, you can also do this in the Storyboard if you first add the IBOutlet annotate to the delegate and dataSource properties in TKCalendarMonthView.h
#implementation YourViewController
...
- (void)viewDidLoad
{
[super viewDidLoad];
...
self.calendarMonthView.delegate = self;
self.calendarMonthView.dataSource = self;
I have this code in my project but overtime I run it I get this
#import "ViewController.h"
#import "TapkuLibrary.h"
#import "TKCalendarMonthView.h"
#interface ViewController () <TKCalendarMonthViewDataSource, TKCalendarMonthViewDelegate>
#property (weak, nonatomic) IBOutlet TKCalendarMonthView *calendarMonthView;
#end
#implementation ViewController Method
- (void)viewDidLoad {
[super viewDidLoad];
self.calendarMonthView.delegate = self;
self.calendarMonthView.dataSource = self;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
It successfully loads but then stops because of calendarMonthView:marksFromDate:toDate:'in protocol 'TKCalendarMonthViewDataSource' not implemented
I guess my question is how do I connect TKCalendarMonthView delegate and data source in my viewDidLoad because thats what I didn't do in the instructions since I don't know how and I think that's what's causing this.
Thanks for your time!
READ IT AGAIN, it says not implemented.
That means, you need to implement calendarMonthView:marksFromDate:toDate: in your code...
Regarding delegate, you have already done by use of below statements in viewDidLoad.
self.calendarMonthView.delegate = self;
self.calendarMonthView.dataSource = self;
You need to implement this method "calendarMonthView:marksFromDate:toDate" of DataSource protocol
I'm trying to separate the UITextViewDelegate methods from the main class of my project, I created a class to manage the delegate methods, but I can not change the values of the IBOulets from the main class.
I made a test project with a ViewController and a TextFieldController, in the Storyboard I add a text field and a label. What I want to do is change the text of the label when I start to write in the text field. Here is the code:
ViewController.h:
#import <UIKit/UIKit.h>
#import "TextFieldController.h"
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *textField;
#property (strong, nonatomic) IBOutlet UILabel *charactersLabel;
#end
ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) TextFieldController *textFieldController;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_textFieldController = [[TextFieldController alloc] init];
_textField.delegate = _textFieldController;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
TextFieldController.h:
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface TextFieldController : UIViewController <UITextFieldDelegate>
#end
Text Field Controller.m:
#import "TextFieldController.h"
#interface TextFieldController ()
#property ViewController *viewController;
#end
#implementation TextFieldController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL) textFieldShouldBeginEditing:(UITextField *)textField {
NSLog(#"hello");
_viewController.charactersLabel.text = #"hello";
return YES;
}
#end
When I start writing in the text field the message "Hello" is printed in the log, but the text of the label does not change. I want to know how to change the label text value from the other class.
First change the TextFieldController.h like this:
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface TextFieldController : NSObject <UITextFieldDelegate>
{
}
#property (nonatomic, assign) ViewController *viewController;
#end
Then change your TextFieldController.m file like this:
#import "TextFieldController.h"
#interface TextFieldController ()
#end
#implementation TextFieldController
- (BOOL) textFieldShouldBeginEditing:(UITextField *)textField {
NSLog(#"hello");
self.viewController.charactersLabel.text = #"hello";
return YES;
}
#end
In the ViewController.m do like that:
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) TextFieldController *textFieldController;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_textFieldController = [[TextFieldController alloc] init];
_textFieldController.viewController = self;
_textField.delegate = _textFieldController;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
This will work but I personally dont like that way you took.
Good luck :)
It's failing because _viewController is nil. You need to assign the viewController property in your delegate in order to support the two way communication.
Also, I'd strongly recommend you make your delegate object a subclass of NSObject, and not UIViewController. It does nothing with controlling views. You can just manually instantiate it in your ViewController objects viewDidLoad.
In TextViewController I don't see where the viewController property (_viewController ivar) is being set so it is probably nil. You should set it when you create the TextViewController instance.
When you are navigating to other controllers using storyboad's segue then you need to implement prepareForSegue method to initialised its properties as follows
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"segue for textFieldController"])
{
TextFieldController *_textFieldController = [segue destinationViewController];
_textField.delegate = _textFieldController;
}
}
But I was wondering, why are you setting textFieldDelegate here, why can't you set in TextFieldController's viewDidLoad method as then you didn't you to implement above prepareForSegue method call?
Besides you are keeping strong reference of each other and you are creating strong retain cycle.
One more thing, following code
_textField.delegate = _textFieldController;
will not work, until textFieldController is loaded and its viewDidLoad method is being called as you are only initialising it but its outlets will not be connected until view is loaded into navigation stack.
Could anyone please help me in the following problem?
I new in Xcode, I'm still learning it, but I could figure out how to pass data between ViewControllers.
Here is my working code:
FirstViewController.h:
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
- (IBAction)displayView:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *lblFirst;
#end
FirstViewController.m:
#import "FirstViewController.h"
#import "SecondViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize lblFirst;
SecondViewController *secondViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondViewController.forwarded_lblFirst = lblFirst;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)displayView:(id)sender {
[self.view addSubview:secondViewController.view];
}
#end
SecondViewController.h:
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
#property (nonatomic, retain) UILabel *forwarded_lblFirst;
#property (weak, nonatomic) IBOutlet UITextField *txtSecond;
- (IBAction)btnReturn:(id)sender;
- (IBAction)txtSecond_DidEndOnExit:(id)sender;
- (IBAction)btnSecond:(id)sender;
#end
SecondViewController.m:
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize forwarded_lblFirst;
#synthesize txtSecond;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnReturn:(id)sender {
[self.view removeFromSuperview];
}
- (IBAction)txtSecond_DidEndOnExit:(id)sender {
forwarded_lblFirst.text = txtSecond.text;
[txtSecond resignFirstResponder];
[self.view removeFromSuperview];
}
- (IBAction)btnSecond:(id)sender {
forwarded_lblFirst.text = txtSecond.text;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[txtSecond resignFirstResponder];
}
#end
This code is working perfectly, the text what I enter on the SecondViewController's textbox (after pressing the btnSecond or simply the Return key on the keyboard) appears as the text of the Label on the FirstViewController.
My problem is, that when I do the exact same thing, but with a Tab Bar Application, the Label on the First tab won't change.
I can use the exact same code (except the changing-views part, because I handle on the first app with programmed buttons, but for the Tab-Bar-App there are already the buttons), there are also two ViewControllers for the Tab Bar App, too (one for each tabs).
So the code is the same, line by line, character by character, and the files are the same, too, for both apps (View-Based, Tab-Bar).
Why isn't my code working? How can I solve this problem?
Thank you!
You need to read some articles about transferring data between views:
Passing Data between View Controllers
Storyboard
passing data between views
iPhoneDevSDK
Talking about your problem, try this approach:
Add the property to your Application Delegate.
When assigning the property do something like:
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
delegate.myProperty = #"My Value";
then, in your different tabs, you can retrieve this property in the same manner:
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *valueInTab = delegate.myProperty;
A different approach to this problem is to use an independent data model.
Create an object that provides access and manipulation methods for all the data that your application needs to save, load, share, or use in any non-trivial way. Make the object available either as a Singleton or as a property of the application delegate. When changes happen, have the data model update application state. When something needs to be displayed, have the controller fetch it from the data model and send it to the view (MVC!).
If you don't store things in controllers that actually belong in the model, you never need to worry about passing information from controller to controller.
You need to use tabbardelegate in your secondview controller, assuming that you use this schema
FirstViewController.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
#property (strong, nonatomic) NSString *content;
#end
SecondViewController.m
#import "SecondViewController.h"
#import "FirstViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.tabBarController.delegate = self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController{
if ([viewController isKindOfClass:[FirstViewController class]]){
FirstViewController *vc =( FirstViewController *) viewController;
vc.content = #"your content";
}
return TRUE;
}
I am working on a very basic app that displays a popover when the user is entering text into a UITextField. Unfortunately, the popover is not showing up and the default keyboard is appearing (which shouldn't). Here is my relevant code below:
NumberPadViewController.h
#import <UIKit/UIKit.h>
#import "NumberViewController.h"
#interface NumberPadViewController : UIViewController <UITextFieldDelegate> {
IBOutlet UITextField *numTest;
}
#property (nonatomic, strong) NumberViewController *numberPicker;
#property (nonatomic, strong) UIPopoverController *numberPickerPopover;
#end
NumberPadViewController.m
- (BOOL)textFieldShouldBeginEditing:(UITextField *) textField
{
// Create popover controller if nil
if(_numberPickerPopover == nil){ //make sure popover isn't displayed more than once in the view
_numberPickerPopover = [[UIPopoverController alloc]initWithContentViewController:_numberPicker];
}
[_numberPickerPopover presentPopoverFromRect:numTest.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
return NO;
}
My popover class is called NumberViewController.h
#interface NumberViewController : UIViewController {
}
#property (strong, nonatomic) IBOutlet UIButton *oneButton;
NumberViewController.m
#import "NumberViewController.h"
#interface NumberViewController ()
#end
#implementation NumberViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
NSInteger buttonHeight = _oneButton.frame.size.height * 4;
NSInteger buttonWidth = _oneButton.frame.size.width * 3;
self.contentSizeForViewInPopover = CGSizeMake(buttonWidth, buttonHeight);
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I have created the UITextField in Storyboard, and set the delegate there. Can anyone see what I am doing wrong?
Thanks in advance to all who reply.
Ensure the textFieldShouldBeginEditing delegate method is being called. The rest of the code looks correct.
This is just a thought, but when a popover does a popover thing i think it becomes first responder, but your text view is first responder... so it might not be able to over do it.... if this is the error then before you tell the popover to appear you can say [textView resignFirstResponder]; and see if that helps.... it's just a thought though not 100% sure i will have to do some testing ~
also check to see if _numberPicker is not nil aswell i don't know what happens if you try to display a popover with no controller but you can see if that's the problem
Seeing as your popover isn't represented in the storyboard (if I read your post right) I think you need to add the popover view as a subview in code. Something like:
[self addSubview:_numberPickerPopover];
There are potentially a few places to do this. Probably makes the most sense in your textFieldShouldBeginEditing: method, after you've inited it.