So I have a basic app, here is how it works. I have a root view controller called A and a table view controller called B. And when the user selects a row in B I pop back to the root view controller A.
And what I am trying to do is to pass the data of the row that was selected as a NSString back to the root view controller A. And then use this string to "do something" depending on the string.
I have tried using the NSNotification method but then I can't use the string to do something.
Heres what I have tried:
//tableViewB.m
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[[NSNotificationCenter defaultCenter] postNotificationName:#"passData" object:[[_objects objectAtIndex:indexPath.row] objectForKey:#"title"]];
[self.navigationController popToRootViewControllerAnimated:YES];
}
//rootViewA.m
-(void)dataReceived:(NSNotification *)noti
{
NSLog(#"dataReceived :%#", noti.object);
}
-(void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataReceived:) name:#"passData" object:nil];
}
What I am trying to do is some more like you can do when you push a viewController and use the perpareForSegue Method.
Thanks in advance for your help.
You're doing the right thing but with the wrong parameters. The object: param in the notification post is the sending object. There's another post method that allows the caller to attach userInfo: as follows:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// notice the prettier, modern notation
NSString *string = _objects[indexPath.row][#"title"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"passData"
object:self
userInfo:#{"theString" : string }]
[self.navigationController popToRootViewControllerAnimated:YES];
}
On the receiving end, just get the data out of the notification's user info with the same key:
-(void)dataReceived:(NSNotification *)notification {
NSLog(#"dataReceived :%#", notification.userInfo[#"theString"]);
}
Use delegate: it would be better than NSNotification
tableView.h:
#protocol tableViewDelegate
-(void) tableViewSelectRowWithString:(NSString*)str;
#end
#interface tableView:UITableViewController //or something like this
#property(nonatomic,weak) id<tableViewDelegate> delegate;
tableView.m:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.delegate tableViewSelectRowWithString:#"your string"];
[self.navigationController popToRootViewControllerAnimated:YES];
}
-(void) dealloc{self.delegate = nil;}
//rootViewA.h
#interface rootViewA : UIViewController<tableViewDelegate>
//rootViewA.m
//When create tableView and push view:
tableView *t = ....;
tableView.delegate = self
-(void) tableViewSelectRowWithString:(NSString*)str{//use string}
Try this may be help full
MyAController *myController = (MyAController *)[self.navigationController.viewControllers objectAtIndex:0];
myController.myText = #"My String" ;
[self.navigationController popToViewController:myController animated:YES];
I have use many time this .. It's working fine ..
Note : replace your class name and string in this .
Thanks :)
Related
I have MainController which is the (UIViewController) main view in the app, and MenuController which is a UITableView.
In the MainController.h
- (void) menu1:(NSInteger ) row;
In the MainController.m
- (void) menu1:(NSInteger ) row{
switch(row){
case 0:
//DO SOMETHING.......
break;
default:
break;
}
}
I want "menu1" to make action when I click a cell in the MenuController.
I made this: (in MenuController.m)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == 0){
[menu1:indexPath.row]; //<=== What should I do to make it work ?
}
}
You can access the method using your UINavigationController.
So in your MainController.h, add this:
- (void) menu1:(NSInteger)row;
Now in your MenuController.m:
#import "MainController.h";
and change didSelectRowAtIndexPathto:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == 0){
MainController *mC = [self.navigationController.viewControllers objectAtIndex:0]; // Change the objectAtIndex number to the number of your MainController in the navigationController view hierarchy
[mC menu1:indexPath.row];
}
}
Your menu1 method is an instance method on the MainController, so you'll want to send the message to an instance of that class. Like so:
MainController *mainVC = [[MainController alloc] init];
[mainVC menu1:indexPath.row];
Or, if you were to define your menu1 method as a class method, you could just send the message to the MainController without initializing. Use plusses instead of minuses when defining your menu1 method, then call like this:
[MainController menu1:indexPath.row];
However, this probably isn't the best way for you to pass data back and forth between View Controllers. You'll likely want to at the very least set a property on your MenuController, and then send the message to that (of course, setting the property wherever it makes sense).
[self.mainController menu1:indexPath.row];
But another really good method for calling a method on another View Controller is the delegate pattern, but I'll leave that up to you to research on your own.
https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
in your MenuController.h
You get back to your previous view like this.
[[self navigationController] popToViewController:yourController animated:YES];
or
[self.navigationController popToRootViewControllerAnimated:animated];
and use UINotificationCenter or Delegate pattern.
If you have no idea about them UINoitifcation is easier but Delegate pattern is better in this situation. I ll explain how to use UINotificationCenter.
Throw notification from your menuController like this.
[[NSNotificationCenter defaultCenter]
postNotificationName:#"yourNotificationName"
object:nil ];
This code is for catch notification write it down to your viewappear method.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(yourMethodAboutWhatYouWantToDoAfterCatchNotification:)
name:#"yourNotificationName"
object:nil ];
Do what you want to do after catch it
- (void) yourMethodAboutWhatYouWantToDoAfterCatchNotification:(NSNotification *) notification
{
// do your job.
}
And in viewDidDisappear remove it. Or you can catch it multiple times after time.
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"yourNOtificationName" object:nil];
There are two ways to achieve this.
Implement a protocol, whose implementation should be there in MainController.m
In that implementation, you should call the method which you have in MainController.m.
Set the table views delegate to MainController instead of setting it as self (MenuController)
This will help to implement the delegate methods directly in the MainController.m, from which the menu1 method can be called.
Provided code below for the first way:
Create a protocol file, which will help to make a callback, and create a method.
//
// utilProtocol.h
// tempProject
//
#protocol utilProtocol <NSObject>
#optional
-(void)captureCellSelectForRow:(int)rowNumber;
#end
Now Import this file inside in MenuController.
#import "utilProtocol.h" // Inside MenuController
And create a variable for the protocol.
#property(readwrite,assign)id<utilProtocol>utilDelegate;
In MainController.h, Import the utilProtocol.h and have its delegate into it.
Don't forget to add utilProtocol
#import <UIKit/UIKit.h>
#import "utilProtocol.h"
#interface ViewController : UIViewController <utilProtocol>
#end
Now Implement the method in MainController.m
-(void)captureCellSelectForRow:(int)rowNumber
{
[menu1:rowNumber];
}
Also set the delegate for it when the tableVIew's object is created in MainController.m
[tableViewObject setUtilDelegate:self];
Now when ever didSelectRowAtIndexPath is called in MenuController, just call the method using protocol.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == 0){
[utilDelegate captureCellSelectedForRow:indexPath.row];
}
}
Using this, the method inside MainController can be called.
I've spent a few hours on this trying to work it out myself but I give up!
I have a master-detail arrangement where the user input screen needs to call a function on another class to post to a web service. Upon completion of the asynchronous call, the class will then call a specified function. In this case, I'm just testing and all I want to do is go back to the main screen after the user input is accepted by the web service.
When the uses taps a button on the input screen (SetLocationViewController), the asynchronous operation is called in the class APIPostClass. After it is complete, I want SetLocationViewController to segue back to MasterViewController.
In APIPostClass.m in (called after the asynchronous op finishes)
-(void)callWhenDone {
NSLog(#"callWhenDone loaded.");
SetLocationViewController *SLVClassInstance = [[SetLocationViewController alloc] init];
[SLVClassInstance doSegue];
}
In SetLocationViewController.m
-(void) doSegue {
NSLog(#"doSegue loaded");
[self performSegueWithIdentifier:#"SetLocationViewControllerManualUnwind" sender:self];
}
Calling doSegue from an action on SetLocationViewController.m does work so I know my segue is ok but the above doesn't work. I get the error reason: 'Receiver () has no segue with identifier 'SetLocationViewControllerManualUnwind''
I'm guessing the reason is because of the alloc init way of initialising of the VC, but I don't know any better. Thus, how can I call a function on another class as if it was being called by it's own class?
Create a delegate it would be much more reliable and fast than Notifications.
#protocol APIPostDelegate <NSObject>
#required
-(void)OnRequestSucess;
#end
In your APIPost add new property for delegate
#interface APIPost : NSObject
#property (weak) id<APIPostDelegate> delegate;
In SetLocationViewController implement APIPostDelegate
SetLocationViewController.h
SetLocationViewController :NSObject<APIPostDelegate>
SetLocationViewController.m
-(void)OnRequestSucess
{
[self doSegue];
}
before you make call to method on APIPost, assign self to delegate property.
APIPost *apipost=[[APIPost alloc]init];
apipost.delegate=self;
[apipost <your api method>];
APIPost.m
[self.delegate OnRequestSucess];
Hope this helps.
There are a few methods to make it happens:-
Use Delegate
Use NSNotification.
The way described by Artur above (For SplitViewController Only - iPad)
You should use delegate whenever it is possible but it might not be too straight forward. NSNotification is more straight forward but it is not a good practice and not a good programming style.
I will only share the NSNotification method as it is easier to implement.
In SetLocationViewController.m
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSegue) name:#"calldoSegue" object:nil];
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter]removeObserver:self name:#"calldoSegue" object:nil];
}
-(void) doSegue {
NSLog(#"doSegue loaded");
[self performSegueWithIdentifier:#"SetLocationViewControllerManualUnwind" sender:self];
}
In APIPostClass.m
-(void)callWhenDone {
NSLog(#"callWhenDone loaded.");
[[NSNotificationCenter defaultCenter]postNotificationName:#"calldoSegue" object:nil];
}
The above code should work but again, this is not a good practice. You should try to learn the Delegate method.
The answer is here: Performing segue from another class
In my APIPostClass.h, I setup the view controller:
#interface APIPostClass : NSObject {
SetLocationViewController *setLocationViewController;
}
#property(nonatomic, strong) SetLocationViewController *setLocationViewController;
#end
In my APIPostClass.m, I synthesize it:
#synthesize setLocationViewController;
then, instead of this (as in my question):
-(void)callWhenDone {
NSLog(#"callWhenDone loaded.");
SetLocationViewController *SLVClassInstance = [[SetLocationViewController alloc] init];
[SLVClassInstance doSegue];
}
I have:
-(void)callWhenDone {
NSLog(#"callWhenDone loaded");
[self.setLocationViewController doSegue];
}
Over in SetLocationViewController.m, the segue method remains unchanged:
-(void) doSegue {
NSLog(#"doSegue loaded");
[self performSegueWithIdentifier:#"SetLocationViewControllerManualUnwind" sender:self];
}
But when I call my API, I need to "attach" (forgive my terminology) the view controller to it. This is what I had:
- (IBAction)btnTestAPICall:(id)sender {
NSLog(#"User tapped API button");
APIPostClass *APIPostClassInstance = [[APIPostClass alloc] init];
[APIPostClassInstance APICall: ... ....
}
But this is what works after bringing all of the above:
- (IBAction)btnTestAPICall:(id)sender {
NSLog(#"User tapped API button");
APIPostClass *APIPostClassInstance= [[APIPostClass alloc] init];
UIViewController *currentVC=self;
APIPostClassInstance.setLocationViewController = currentVC;
[APIPostClassInstance APICall: ... ...
I hope this will help someone else!
I just passed the data from one ViewController to another viewController using below process
I want to display the data in UITextField in SecondViewController. Passed value must be a int value.
[[NSNotificationCenter defaultCenter] postNotificationName:#"sendString" object:userID];
Change viewDidload method in which you want to get string.
-(void)viewDidLoad:
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ThingYouWantToDoWithString:) name:#"sendString" object:nil];
}
- (void)ThingYouWantToDoWithString:(NSNotification *)notification{
passedUserID = [notification object];
NSLog("%#", passedUserID);
}
#property(nonatomic,assign) NSInteger userid;
in both the view controllers
then
SecondViewController *svc=[[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
svc.passedUserID=userId;
[self.navigationController pushViewController:svc animated:YES];
NSLog(#"%#",passedUsrID);
and don't forget mention %d for integer.
We can pass data between view controllers using properties
consider two classes A & B, If We need to transfer data from A to B
write the property in B
#property(nonatomic,retain) NSString *userid;
and while pushing to B
B *b = [[B alloc] init];
b.userid = INTERGER_YOU_NEED_TO_PASS;
[self.navigationController pushViewController:b animated:YES];
and in B.m
-(void)viewDidLoad
{
aTextField.text = self.userid;
}
If you want to use notification center as you have done above than
chage the line of your code as follws
- (void)ThingYouWantToDoWithString:(NSNotification *)notification{
passedUserID = [notification object];
NSLog("%#", passedUserID);
yourTextField.text = [NSString stringWithFormat: #"%#",passedUserID];
}
I have a view controller, that is called from several other view controllers. This view controller contains a UITextField, which collects different information, depending on which view controller has called it.
The information has to be stored after it was collected. To be as independent as possible, the method to store the information should be located in the calling view controller.
Thus, I use the following code in the method to collect the information:
- (void) collectContent
{
NSString *info = [textField text];
[textField resignFirstResponder];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NewValueA" object:info];
[[self navigationController] popToRootViewControllerAnimated:YES];
}
In the calling view controller, I have the following line in its init method:
- (id) init
{
...
if (self)
{
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(storeNewValueA) name:"NewValueA" object:nil];
}
return self;
}
But now, I want to use this view controller from another view controller, to collect ValueB or ValueC. How do I reference the calling view controller to call a method for storing the collected value just there? I want to decide, that if the view controller was called from vcB, the entered value must have been valueB, an so on...
You can create a protocol for this commonly used view controller and implement this protocol by the calling view controllers :
#protocol TextFieldViewControllerDelegate : NSObject {
- (void)contentCollected;
}
In your text field (the common) view controller define a delegate property:
#property (nonatomic, strong) id<TextFieldViewControllerDelegate> delegate;
The calling view controller will set itself as the delegate and implement the needed method:
- (void) contentCollected:(NSString *)value
{
// Store the value where needed
}
In the common (textfield) view controller notify the delegate when needed :
- (void) collectContent
{
NSString *info = [textField text];
[textField resignFirstResponder];
[_delegate contentCollected:info];
[[self navigationController] popToRootViewControllerAnimated:YES];
_delegate = nil;
}
This way your common view controller does not need to know which view controller called it. Using the delegate the calling view controller will be notified.
Hm, if you want to persist this simple string even when the application was close, I would use NSUserDefaults:
Store the string:
[[NSUserDefaults standardUserDefaults] setObject:#"MyValue" ForKey:#"USER_DEFAULTS_VIEWCONTROLLER_VALUES"];
[[NSUserDefaults standardUserDefaults] synchronize];
Get the string:
[[NSUserDefaults standardUserDefaults] objectForKey:#"USER_DEFAULTS_VIEWCONTROLLER_VALUES"];
In the iOS application, I need to pass an object from one view controller to another and then switch to the new view controller. The goal is to simply relay the information. However, the object becomes NULL and is devoid of any information when the next view showed up. what? could be the reason?
Below is part of my code.
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BIDCourse *course = self.courses[indexPath.row];
NSString *sub = course.subject;
BIDSubjectMainTwoViewController *subjectController=[[BIDSubjectMainTwoViewController alloc] init];
subjectController.title = sub;
subjectController.course=course;
[self.navigationController pushViewController:subjectController animated:YES];
}
BIDCourse *course is my custom subclass to NSObject, which has some NSStrings, and it's not nil, but when I pass it onto the next view controller, it becomes NULL.
I have met a problem like you , when I pass a object to another viewController , it has been released.
I think this is a defect of ARC , ARC think your subjectController may be no use , so it release the subjectController. Make your subjectController as a property , so ARC will not release when you pass it to another viewController.
such as :
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BIDCourse *course = self.courses[indexPath.row];
NSString *sub = course.subject;
_subjectController=[[BIDSubjectMainTwoViewController alloc] init];
_subjectController.title = sub;
_subjectController.course=course;
[self.navigationController pushViewController:_subjectController animated:YES];
}
Could the problem lie in BIDSubjectMainTwoViewController.m?
subjectController.title = sub;
subjectController.course = course;
in your BIDSubjectMainTwoViewController.m file, did you #synthesize your properties?
#synthesize title = _title;
#synthesize course = _course;
You could also pass the variables along with an init function ala:
BIDSubjectMainTwoViewController.h
- (id)initWithTitle:(NSString *)title
andCourse:(BIDCourse *)course;
BIDSubjectMainTwoViewController.m
- (id)initWithTitle:(NSString *)title
andCourse:(BIDCourse *)course
{
self = [super init];
if (self) {
// Custom initialization
_title=title;
_course = course;
}
return self;
}
I encountered this similar behavior. My solution was to make a copy of the object. For your case:
BIDCourse *course = [self.courses[indexPath.row] copy];
And make sure you implement copyWithZone in the BIDCourse .m fike.
-(id)copyWithZone:(NSZone*) zone
Try this:
BIDSubjectMainTwoViewController.h
NSString *strTemp;
#property(nonatomic,retain) NSString *strTemp;
-(void) setValuestrTemp : (NSString *) str;
.m file
#synthesize strTemp;
// Write setter method
-(void) setValuestrTemp : (NSString *) str {
self.strTemp = str;
}
and use strTemp in viewWillAppear.
//Now use it
BIDSubjectMainTwoViewController *subjectController=[[BIDSubjectMainTwoViewController alloc] init];
[subjectController setValuestrTemp:sub];
[self.navigationController pushViewController:_subjectController animated:YES];
Use
NSString *sub = [course.subject copy];
subjectController.course=[course copy];
implement delegate in BIDCourse
-(id)copyWithZone:(NSZone*) zone