How to update a Label's text of ViewController from SecondViewController - ios

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?

Related

Delegate method not being called, setting delegate to self?

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.

Passing data between UIViewController and UITableViewController not connected via segue

I followed the advice at this link Changing the value of a property of another UIViewController
I have a Login page that is connected to a github project that I downloaded called SWRevealViewController. It essentially provides a slide out menu for the application. I need to pass a value from my Login Page to the initial Page that is loaded with the SWRevealViewController. Can't seem to get it working. Can anyone point me in the right direction? What I have tried is the following:
LoginViewController.m
#import "LoginViewController.h"
#import "ProfileTableViewController.h"
#interface LoginViewController ()
#property (strong, nonatomic) ProfileTableViewController *secondViewController;
#end
#implementation dbViewController
-(id)init
{
if (self = [super init]) {
self.secondViewController = [[ProfileTableViewController alloc] init];
}
return self;
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
if([identifier isEqualToString:#"Login"]){
[self login:sender];
self.secondViewController.idNumber = _IDNumber;
_secondViewController.idNumber = _IDNumber;
return YES;
}
}
LoginViewController.h
#import <UIKit/UIKit.h>
#import <sqlite3.h>
#import "ProfileTableViewController.h"
#interface LoginViewController : UIViewController
#property (strong, nonatomic)NSString *IDNumber;
#end
ProfileTableViewController.h
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
#import <sqlite3.h>
#interface ProfileTableViewController : UIViewController <CLLocationManagerDelegate, UITextFieldDelegate>
#property (nonatomic) NSString *idNumber;
#end
ProfileTableViewController.m
#import "ProfileTableViewController.h"
#import "SWRevealViewController.h"
#import "ProfileEditFieldController.h"
#interface ProfileTableViewController ()
#end
#implementation ProfileTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(_idNumber);
}
#end
Just to add clarification, the two blue controllers are the Login, and the three controllers on the right, one of them is the controller I would like to pass the value for
You should do it like this instead:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
ProfileTableViewController *pvc = (ProfileTableViewController*)[segue destinationViewController];
pvc.idNumber = _IDNumber
}
Hope that works! :)
UPDATE:
I think what you need is NSUserDefaults, use it, and store the ID. You gonna need it in more than one controller I believe, and its easy to store data on.
NSUserDefaults
It's not 100% clear from what you've shown but I'm assuming that these view controllers are defined in a storyboard and that the "Login" segue is also defined in the storyboard and causes a transition from LoginViewController to ProfileTableViewController.
One thing I am confused about is why the #implementation line in LoginViewController.m says "dbViewController" but I'm assuming that's a typo and that it should be "#implementation LoginViewController".
So, assuming all of that is true, there are a couple of problems in your code. First, you shouldn't be creating the ProfileTableViewController in LoginViewController's init method. iOS will automatically alloc and init the ProfileTableViewController when the segue is performed.
Second, instead of using shouldPerformSegueWithIdentifier:sender:, you want to use prepareForSegue:sender:. When that method is called, iOS will have already created the ProfileTableViewController and you can set your property.
So, what you want is something like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Login"])
{
ProfileTableViewController * viewController = segue.destinationViewController;
viewController.idNumber = _IDNumber;
}
}

UITextFieldDelegate not working

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.

Passing data between ViewControllers is working, but not with Tab Bar Controller

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;
}

Custom delegation to pass data between tab bar view in iOS not working

I am facing a problem related to delegation.I have 2 tabView. In first tabview i have 2 textfield and a button(to trigger the delegate method) and in the second tabview i have 2 label to display the content of textfield in the first tabview.Whats wrong with my code???
For first tabviewA the .h file
#import <UIKit/UIKit.h>
#class ViewControllerA;
#protocol ViewControllerADelegate <NSObject>
-(void)sayHello:(ViewControllerA*)viewController;
#end
#interface ViewControllerA : UIViewController<UITextFieldDelegate>
#property (nonatomic,strong)IBOutlet UITextField *textFieldFirst;
#property (nonatomic,strong)IBOutlet UITextField *textFieldSecond;
#property (nonatomic,strong)id<ViewControllerADelegate>delegate;
-(IBAction)next:(id)sender;
#end
and the .m file
#import "ViewControllerA.h"
#interface ViewControllerA ()
#end
#implementation ViewControllerA
#synthesize textFieldFirst=_textFieldFirst;
#synthesize textFieldSecond=_textFieldSecond;
#synthesize delegate=_delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
_textFieldFirst.delegate=self;
_textFieldSecond.delegate=self;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return NO;
}
-(IBAction)next:(id)sender
{
[self.delegate sayHello:self];
}
#end
And for the second tabbarViewB the .h file
#import <UIKit/UIKit.h>
#import "ViewControllerA.h"
#interface ViewControllerB : UIViewController<ViewControllerADelegate>
#property (nonatomic,strong)IBOutlet UILabel *labelFirst;
#property (nonatomic,strong)IBOutlet UILabel *labelSecond;
#end
and the .m file
#import "ViewControllerB.h"
#interface ViewControllerB ()
#end
#implementation ViewControllerB
#synthesize labelFirst=_labelFirst;
#synthesize labelSecond=_labelSecond;
- (void)viewDidLoad
{
[super viewDidLoad];
ViewControllerA *viewControllerA=[self.tabBarController.viewControllers objectAtIndex:0];
viewControllerA.delegate=self;
// Do any additional setup after loading the view.
}
-(void)sayHello:(ViewControllerA *)viewController
{
_labelFirst.text=viewController.textFieldFirst.text;
_labelSecond.text=viewController.textFieldSecond.text;
}
#end
N.B: I have tried tabView B to tabview A with the same process and that worked fine. The reverse (i.e from A to B)is not working at all.Thanks
ViewControllerB sets itself as the delegate in its viewDidLoad method. This method is called as soon as the View Controller has loaded the view it manages (into its view property). That view is loaded only when someone tries to access the view controller's view property for the first time. See here. There's a good chance that ViewControllerB's view has not been loaded yet, so the viewDidLoad method has not been called yet.
If you override the awakeFromNib method like so:
(void) awakeFromNib {
[super awakeFromNib];
ViewControllerA *viewControllerA=[self.tabBarController.viewControllers
objectAtIndex:0];
viewControllerA.delegate = self;
}
it should work, since that method will be called when the view controller is initialized.

Resources