I have this problem:
2 views called A and B, both are TableViewController.
A listen for notification, B sends notification
I'm not going to explain in detail, but for sake of simplicity it's like this:
A can display multiple types of data, B has a list of these types, selecting one row from B makes A load in it's tableView the right list of data.
While i'm in B i'm sending a notification like this
NSNumber *section = [NSNumber numberWithInt:indexPath.row];
NSDictionary *infoDictionary = [NSDictionary dictionaryWithObject:section
forKey:#"CurrentTableView"]
NSString *UpdateTableView = #"UpdateTableView";
[[NSNotificationCenter defaultCenter] postNotificationName:UpdateTableView
object:self
userInfo:infoDictionary];
Now in A i'm listening like this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateTableView:)
name:#"UpdateTableView"
object:nil];
which calls the method
#property (strong, nonatomic) NSNumber *section;
// :::: Some code here ::::
- (void)updateTableView:(NSNotification*)note
{
_section = [[note userInfo] valueForKey:#"CurrentTableView"];
NSLog(#"%d",[_section intValue]);
[self.tableView reloadData];
}
and the NSLog works fine, i mean it prints the right value.
I'm using _section to discriminate in the TableView delegate methods what kind of data to load.
The problem is that the call to this method (after the notification is received) happens BEFORE the view is actually reloaded (viewDidAppear and so) which set my #property _section to 0, in this way every time my TableView loads the data standing behind the [_section intValue] == 0.
How could i solve this? I need something that don't gets reset every time the view loads itself. Any suggestion?
EDIT: navigation controller to move from B to A
MenuNavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"contentController"];
AViewController *homeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeController"];
navigationController.viewControllers = #[homeViewController];
Do you really need NSNotification ? Notifications are used where you want to update a view, and by update i mean that the view is already created.
What i would do is fairly simple, in B i would set your NSNumber *section as an ivar (if you display controller A some place different from where you post you notification). When you posted your notification you would just instantiate your ivar.
Then A would have a similar variable and when you want to display A :
AViewController *homeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeController"];
homeViewController.section = section;
navigationController.viewControllers = #[homeViewController];
Perhaps your property section isn't synthesizing to use your instance variable _section. Does your synthesize line look like this:
#synthesize section=_section;
Otherwise the compiler will implicitly create an instance variable name section
UPDATE
As #Justafinger mentioned, as of Xcode 4.4, the ivar will be named _section if you completely leave out the #synthesize line. If you do explicitly synthesize, however, and don't assign the ivar name in the #synthesize line the ivar name will default to section.
Related
I am very new to iOS and overwhelmed with resources I find online. My use case is simple
a.) ViewController parent has label called categoryLabel. A tap on label category opens a new view View 1
b.) View 1, shows all groups. Lets says A, B, C. This will be shown on table
c.) when user click on any group A, B or C, a new view View 2 appears with all categories in that group. For example, user clicks on A and on View 2 user sees categories A1, A2, A3.
d.) Now when user clicks on any specific category, how does that goes back to ViewController parent and assigns to categoryLabel?
I do not know what is the best way to approach this design.
Any guidance is very much appreciated
hope this will help
let take an example , your are going from A -> B and want send some data from B to A , there are many technique to do that but using delegate method and block are nicer way.
delegate way :-
in your B.h file
#protocol yourDelegate <NSObject>
-(void)whichCategoryClicked:(NSString *)categoryName;
#end
#interface B : UIView
#property(nonatomic, assign)id<yourDelegate> delegate;
in your B.m
just call this delegate method after Clicking particular category.
[self.delegate whichCategoryClicked :#"Category_name"];
in your A.h
assign it as delegate and import the above class
#interface A.h : UIViewController<yourDelegate>
and in Implement this method in A.m
first in your viewdidload
{
B *objB = [[B alloc]init];
objB.delegate = self;
}
-(void)whichCategoryClicked:(NSString *)categoryName
{
categoryLabel.text = categoryName;
}
You can use Local notification for this purpose names as NSNotificationCenter in iOS. Which works as follows:
To send a notification that is from the view on which you are and want to send some value from that view, use below code:
NSDictionary *dict;
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationKey" object:nil userInfo:dict];
and now on any of the view controller, you can add observer on viewDidLoad of that class as:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(methodToCall:)
name:#"NotificationKey"
object:nil];
Now call method written in above line:
- (void)updateImageFromArray:(NSNotification *)notification {
// your dict
NSDictionary *dictUserInfo = [notification userInfo];
}
I'm can see that there is a lot of questions regarding this already, but none of them seems to have given me an explanation to why I cannot access my array from another class.
Here's where I wan't to access the array
(XYZPaymentViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
XYZMateOverviewViewController *test = [[XYZMateOverviewViewController alloc] init];
NSMutableArray *t = test.mates;
NSLog(#"Count of ThatArray: %d", [t count]);
}
Nomatter what - the log writes out 0 !!
In another class (XYZMateOverviewViewController.h) I declare the array
#property (retain) NSMutableArray *mates;
I synthesize the array in the implementation area in XYZMateOverviewViewController.m
#synthesize mates;
I hope you can help me understand what I'm doing wrong :)
If you want to access an array from another ViewController you have to pass the array between the ViewControllers.
For example:
You have two ViewControllers. 'A' and 'B', so u have to do this:
In the 'A' ViewController, is where you have the array that you want to send to another ViewController. Then create an instance of 'B' ViewController and send the array to him:
CODE OF 'A' ViewController:
NSMutableArray *arrayToSend = [[NSMutableArray alloc]init];
BViewController *bViewController = [[BViewController alloc]initWithNibName:#"BViewController" bundle:nil];
BViewController.arrayReceived = arrayToSend;
[self.view addSubView:BViewController.view];
After, in your BViewController you just have to take your arrayReceived and use it.
CODE OF 'B' ViewController:
NSLog(#"%#", [arrayReceived objectAtIndex:0]); // For example.
Hope i did help you, if u have any question tell me.
What are you doing with the mates array in the XYZMateOverviewViewController init method?
If you're not adding any data to it a count of 0 is just right.
With the line
XYZMateOverviewViewController *test = [[XYZMateOverviewViewController alloc] init];
you are creating a completely new instance of XYZMateOverviewViewController—one that has nothing to do with any other part of your application*. What you should be doing instead is getting a reference to the already-set-up instance of the view controller. That instance presumably has its mates property set up with the data you want.
The general topic of sharing data between different parts of your application—and between different view controllers, in particular—should be covered somewhere toward the beginning of any “introduction to iOS programming” book or tutorial.
* Unless you’re doing something funky with singletons or shared state, of course, but I doubt that’s the case here.
Do you alloc/init the mutable array in the init method?
I have an application where A View Controller (A)is called twice in close succession. Now each time it is called, an NSString object is created, and I need this value to be stored in an NSMutableArray that is a public property of ANOTHER View Controller (B).
In A, I create an instance of the second View Controller (B), and using that instance, add the NSString objects into the NSMutableArray which I've created as a public property. Later, when I am inside View Controller B and print the contents of the NSMutableArray property, the array is empty. Why? Here is the code that is inside View Controller A:
-(void)viewDidLoad {
ViewControllerA *aVC = [[ViewControllerA alloc] init];
if (aVC.stringArray == nil) {
aVC.stringArray = [[NSMutableArray alloc] init];
}
[aVC.stringArray addObject:#"hello"];
[aVC.stringArray addObject:#"world"];
for (NSString *wow in aVC.stringArray) {
NSLog(#"The output is: %#", wow);
}
}
Inside my View Controller B class, I have the following code:
- (IBAction)buttonAction:(UIButton *)sender {
NSLog(#"Button selected");
for (NSString *test in self.stringArray) {
NSLog(#"Here are the contents of the array %#", test);
}
}
Now the buttonAction method gets called, as I do see the line Button selected in the system output, but nothing else is printed. Why? One thing I want to ensure is that View Controller A is called twice, which means I would like to see in the output, "Hello World", "Hello World" (i.e. printed twice), and not "Hello World" printed just once.
The other thing I wish to point out is that View Controller B may not be called at all, or it may be called at a later point in time. In any case, whenever View Controller B is called, I would like to have the values inside the array available, and waiting for the user to access. How do I do this?
Your approach is not ideal, potentially leading to a memory cycle, with two objects holding strong pointers to each other.
You can instead achieve your goal in two ways;
Delegate Protocol
This method allows you to set delegates and delegate methods to pass data back and forth between view controllers
in viewControllerA.h
#protocol viewControllerADelegate <NSObject>
- (void)addStringToNSMutableArray:(NSString *)text;
#end
#interface viewControllerA : UIViewController
#property (nonatomic, weak) id <viewControllerADelegate> delegate;
in viewControllerB.m
// create viewControllerA class object
[self.viewControllerA.delegate = self];
- (void)addStringToNSMutableArray:(NSString *)text
{
[self.mutableArray addObject:text];
}
in viewControllerA.m
[self.delegate addStringToNSMutableArray:#"some text"];
Utility Classes
Alternatively you can use a utility class with publicly accessible methods (and temporary data storage). This allows both viewController classes to access a shared data store, also if you use class methods, you don't even need to instantiate the utility class.
in XYZUtilities.h
#import <Foundation/Foundation.h>
#interface XYZUtilities : NSObject
+ (void)addStringToNSMutableArray;
#property (strong, nonatomic) NSMutableArray *array;
#end
in XYZUtilities.m
+ (void)addStringToNSMutableArray
{
NSString *result = #"some text";
[self.array addObject:result];
}
+ (NSArray)getArrayContents
{
return self.array;
}
in viewControllerA.m
NSString *stringFromObject = [XYZUtilities addStringToNSMutableArray];
in viewControllerB.m
self.mutableArray = [[NSMutableArray alloc] initWithArray:[XYZUtilities getArrayContents]];
I'm not sure what kind of a design pattern you are trying to follow but from the looks of it IMHO that's not a very safe one. However, there are many, many ways this could be accomplished.
One thing though, you said that View Controller B may never get allocated and if it is alloc-ed, it will be down the road. So you can't set a value/property on an object that's never been created.
Since you already aren't really following traditional patterns, you could make a static NSMutableArray variable that is declared in the .m of your View Controller B Class and then expose it via class methods.
So it would look like this:
viewControllerB.h
+(void)addStringToPublicArray:(NSString *)string;
viewContrllerB.m
static NSMutableArray *publicStrings = nil;
+(void)addStringToPublicArray:(NSString *)string{
if (publicStrings == nil){
publicStrings = [[NSMutableArray alloc]init];
}
if (string != nil){
[publicStrings addObject:string];
}
}
Then it would be truly public. All instances of view controller B will have access to it. This, of course is not a traditional or recommended way of doing it—I'm sure that you will have many replies pointing that out ;).
Another idea would be to use a singleton class and store the values in there. Then, when or if view controller B is ever created, you can access them from there.
I am new to iOS.I am recently stuck with a problem.
I have a view A and View B. View A has a navigation controller. view A has a button to switch to B.When i am clicking this button every time B creates a new object. how can i track this object to share data between this two view.
Thanks
There are several ways to do this.
You could have a property of B, that A sets before you push. (NSDictionary, Array, String etc)
This not the best way however it would work.
UIViewController *viewB = [[UIViewController alloc]init];
[viewB setMyProperty:#"some data!"];
[self.navigationController pushViewController:viewB animated:YES];
You could also use NSNotificationCenter to pass the object to the next view.
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:index]
forKey:#"index"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification"
object:self
userInfo:dictionary];
The way I usually handle this is to setup and object that holds my data with an associated protocol initialized in my AppDelegate. Then any view that needs to read/write something just grabs a Pointer to that object and runs with it.
#class AppData;
#protocol AppDataProtocol
- (AppData*)theAppData;
#end
in the View you can grab the pointer with this.
-(AppData*)theAppData {
id<AppDataProtocol> theDelegate = (id<AppDataProtocol>)[UIApplication sharedApplication].delegate;
AppData* theData = (AppData*)theDelegate.theAppData;
return theData;
}
and this.
appData = [self theAppData];
You are then able to easily access any property of appData.
-(void)fnButtonA{
ViewB *vcB = [[ViewB alloc] initWithData:DataToB];
[[self navigationController] pushViewController:vcB animated:Yes];
}
In ViewB.m edit the init function to
-(UIViewController *)initWithData:(NSMutableDictionary*)data
I know this question is asked once every two days. I can not see what I am doing wrong though.
I have a storyboard navigation controller based app.
My notification and pop / push segues works well, only thing is I can not add string to parents view NSmutablearray.
I want to add a string object to parent view's nsmutablearray. My decent code does not pass any data.
parent.h
#interface CreaatePlistTableViewController : UITableViewController<UITableViewDelegate, UITableViewDataSource>{
NSMutableArray *presenterList;
}
#property (nonatomic, strong) NSMutableArray *presenterList;
parent.m
NSString * const NOTIF_CreatePlist_UpdateTableview= #"CreatePlist/UpdateTableview";
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Private interface definitions for update tableview
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#interface CreaatePlistTableViewController (private)
- (void)CreatePlistUpdateTableview:(NSNotification *)notif;
#end
#implementation CreaatePlistTableViewController
#synthesize presenterList=_presenterList;
- (void)viewDidLoad
{
[super viewDidLoad];
_presenterList=[[NSMutableArray alloc] init];
// Register observer to be called when logging out
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(CreatePlistUpdateTableview:)
name:NOTIF_CreatePlist_UpdateTableview object:nil];
NSLog(#"Presenter List: %#", _presenterList);
}
- (void)CreatePlistUpdateTableview:(NSNotification *)notif{
NSLog(#"Notification recieved");
NSLog(#"Presenter List: %#", _presenterList);
[_createPlistTableview reloadData];
}
child.h
#interface AddPresenterViewController : UITableViewController<UITextFieldDelegate,UIAlertViewDelegate>{
CreaatePlistTableViewController *crereaatePlistTableViewController;
}
#property(nonatomic,strong) CreaatePlistTableViewController *crereaatePlistTableViewController;
child.m
#synthesize crereaatePlistTableViewController=_crereaatePlistTableViewController;
//finished adding presenter
-(IBAction)finishedAddingPresenter:(id)sender{
//some xml string here
NSLog(#"final result XML:\n%#", writer.XMLString);
_crereaatePlistTableViewController=[[CreaatePlistTableViewController alloc]init];
//add object to parents view data source
[_crereaatePlistTableViewController.presenterList addObject:writer.XMLString];
//dismiss the view
[self.navigationController popViewControllerAnimated:YES];
//notify the parent view to update its tableview
[[NSNotificationCenter defaultCenter] postNotificationName:#"CreatePlist/UpdateTableview" object:nil];
}
Output
Notification recieved
Presenter List: (
)
So notification works when I click the button. But it does not pass object to nsmutablearray.
What I am doing wrong here ? How can I add an object to parent view's nsmutablearray?
It seems everything is good except your alloc of parent view object I am not that familiar with storyboard but You said you are using navigation navigation controller
so change this
_crereaatePlistTableViewController=[[CreaatePlistTableViewController alloc]init];
to
_crereaatePlistTableViewController= [self.navigationController.viewControllers objectAtIndex:0];
It may work I am not sure
You wrote this.
[_crereaatePlistTableViewController.presenterList addObject:writer.XMLString];
Do you ever initialize the array? No. Use the debugger and you will see that at this line the presenterList is nil.
Now as a point of style. Avoid using NSNotificationCenter to pass data or signaling other objects. #TheRonin gave a handy link. You should also look into some tutorials on Segues, because these are solved problems.
This is another related post that you might find interesting.