I have a UIViewController class and a UITableViewController class. Within the UIViewController class I have an NSMutableArray.
I now have the issue of how to load data into my table view, a separate class, I must access the NSMutableArray I used to populate the previous UIViewController class.
I tried using a delegate to access the array in the UIViewControllerClass however the array had "0 objects" and was NULL
I would appreciate some guidance in the right direction here.
You could have one view controller hold a reference to the other view controller and query the public NSMutableArray on it for data. Aaron suggested this and it might be your best solution.
Or.. you have multiple view controllers trying to access the same set of data. Potentially you have other classes which will want to access this data also. You might want to consider pulling the data out of the view controller and storying it in a neutral location. You could store it in the AppDelegate and then reference the app delegates from any place you need it.
id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
NSMutableArray *myData = appDelegate.data;
You could also consider pulling all the logic of your data and the data itself into a separate class and use a Singleton It would allow you to access/manipulate the data fairly easy from anywhere.
The last 2 methods would insulate data from user interface controller objects and prevent the need from potentially unrelated objects needing to hold references to one another. Used properly it will reduce code complexity and mage future changes easier to manage.
Create an NSMutableArray property on your UITableViewController class like so:
#interface CustomTableViewController : UITableViewController
#property (strong, nonatomic) NSMutableArray *dataFromOtherClass;
#end
And then when you transition, perhaps like this, you can set the dataFromOtherClass property:
CustomTableViewController *controller = [[CustomTableViewController alloc] initWithNibName:#"CustomTableViewController" bundle:nil];
controller.dataFromOtherClass = myNSMutableArrayData; // <-- Set data like this
[self.navigationController controller animated:YES];
// Or ...
[self presentViewController:controller animated:YES];
// Etc...
Related
I have two table views that contain a mutable array of objects that can be instantiated by the user with various properties. The class and its properties are within separate .h and .m files that the table views access.
I would like the two table views to look identical so that when a user adds, deletes, or moves an object in one table view, the second table view is immediately updated. I understand that they will be sharing a mutable array, but I'm not sure where to put it so that changes in one table view occurs in the other table view.
Additionally, is this a situation in which I would make one of the table views a subclass of the other?
Suppose there is a controller owns the NSMutableArray object and the two table views.
Use the KVO feature, let the two table view retain (use strong as property) the NSMutableArray object.
Create a new delegates array property like NSArray< id<MyArrayNotifier> > *delegates for the NSMutableArray object's owner, implement the MyArrayNotifier protocol in two table view classes, add the table objects to delegates array. Now you could get notified when your focused message arrived.
First you should make a singleton class that contains that mutable array so you can edit/access it anywhere , whenever you update that array reload the visible tableView , if the other tableView is also visible reload it(either with delegate , observer , notificationCenter), otherwise it will be updated with the last edit when you open the VC that contains it , also you can make a use of viewDidAppear to reload the table inside it as another choice instead of delegates if it's the logic of your app
// .h
#import <Foundation/Foundation.h>
#interface GlobalData : NSObject
#property(nonatomic,retain)NSMutableArray*masterArray;
+(GlobalData*)shared;
#end
// .m
#import "GlobalData.h"
#implementation GlobalData
static GlobalData *instance = nil;
+(GlobalData *)shared
{
#synchronized(self)
{
if(instance==nil)
{
instance = [GlobalData new];
instance.masterArray = [NSMutableArray new];
}
}
return instance;
}
#end
Usage
[GlobalData.shared.masterArray addObject:"strData"];
SettingsStore.h
#interface SettingsStore : IASKAbstractSettingsStore
{
#public
NSDictionary *dict;
NSDictionary *changedDict;
}
- (void)removeAccount;
#end
menuView.m
-(IBAction)onSignOutClick:(id)sender
{
SettingsStore *foo = [[SettingsStore alloc]init];
[foo removeAccount];
[self.navigationController pushViewController:foo animated:YES];
exit(0);
}
I want to call this removeAccount function from menuView.m. But I am getting error.
How to fix it and call this removeAccount.
There are few mistakes in your Code please find them below.
[foo removeAccount]; Calling this method is correct
[self.navigationController pushViewController:foo animated:YES];
Not correct because SettingsStore is not subclass of
UIViewController only subclass of UIViewController can be pushed to
Navigation controller
exit(0); Calling this method is not
recommended by Apple
You are calling removeAccount correctly from your menuView.m file, but there are several issues with your code:
You are treating foo as though it were a UIViewController, and it's actually a member of the SettingStore class. Does the SettingStore class refer to an actual screen, or is it more a data object (for storing settings?). If it's the latter, you don't want to push it on. You can create it, and use it, but the user doesn't need to see it.
You are calling exit(0); you can remove that line. If you want to remove the menuView.m file from your memory, remove references to it (e.g. from its parent view controller).
The menuView.m file is confusing, as in, is it a view or a viewController. An IBAction I would normally stick in a ViewController file, rather than a view file. Your basic design pattern is MVC (Model / View / Controller). In this case, it seems your SettingStore file is a Model (data), the menuView.m is a View and your code is for the Controller bit.
I'm creating an user setup account with 5 steps using storyboard. Each step have a ViewController: 1º)Input for Name, contact etc, 2º) Import photos, 3º)Input, etc 4º)more inputs 5º)Confirmation Page, if the user click "confirm" -> Get all the inputs and upload to Parse.
The only solution i get when i search online for this, is to create a func "Prepare for Segue" and pass the information...But for me, this doesnt make any sense:
If i had 1000 viewcontrollers, the first viewcontroller information will be passsed through all the 1000 viewcontrollers? Why not the nº1000 view controller getting all the information that was left behind? e.g: The viewcontroller nº50 dont need the information about the viewcontroller nº1... This is a nonsense.
Is there any other way to implement this?
Maybe there is a solution since i'm using Parse like when i do:
ParseClass["Name"] = textfield.text
It sends information to Parse and they hold it until i do something like SaveInBackground().
I'm trying to find a solution like this using viewcontrollers. Each viewcontroller send information to parse and hold the information until the "saveinbackground()" happens on Viewcontroller nº5.
Possible? thank you
Yes it is possible. You can use NSUserDefaults for that which will store your info into memory and once it is stored you can use it anywhere in your project.
Like you can store value with it this way:
NSUserDefaults.standardUserDefaults().setObject(yourObject, forKey: "yourKey")
Now you can retrive this info from any where with yourKey like this:
let yourInstance = NSUserDefaults.standardUserDefaults().objectForKey("yourKey")
And you can cast it's type as per your need.
For more Info read Apple Document for NSUserDefaults.
One more way to pass value from one view to another view without segue by using Class or Struct.
You can read more about Classes and Structures from Apple Documentation.
If our inputs are finite, create a model class with properties for it ex:
#interface UserDataInput : NSObject
#property (nonatomic)NSString *name;
#property (nonatomic)NSString *contactNumber;
....
blah blah bla
....
#end
then make it as a singleton class
#implementation UserDataInput
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static UserDataInput *sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});
return sharedInstance;
}
#end
Then set properties from a view controller on leaving that view controller like,
UserDataInput *sharedInput = [UserDataInput sharedInstance];
sharedInput.name = self.nameField.text;
etc....
In final view controller you can access these properties to upload to parse.
Try it.
let yourInstance = NSUserDefaults.standardUserDefaults().objectForKey("yourKey")
NSUserDefaults.standardUserDefaults().synchronize()
I am making master detail application, i have dynamic Detail ViewController. Detail ViewController are changed.
But in every Detail ViewController I have one common method updateInfo I want to call that method
Here is my code
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
UIViewController *controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But it gives me error no method found.
it will work if i use UIViewController name.
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But i dnt want to do above things.
I have tried to explain. Please help
You can use id
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
id controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
You could subclass UIViewController and make a base DetailViewController class that houses common functionality of your detail view controllers. Then you would make all of your detail view controllers subclass DetailViewController instead of UIViewController. This would be a safe way to do it and would also allow you to add extra functionality to your updateInfo method in the specific detail view controllers.
If you want an unsafe way, you could make your controller object of type id. I wouldn't suggest this approach as it relies on your personal knowledge of the code. If someone else (or yourself down the road) sets it to a view controller that doesn't have that method, the code will still try to run and will crash.
UIViewController doesn't have a method named updateInfo, so the compiler will of course complain when you try to send that message to a pointer that's known only to point to an instance of UIViewController. When you use the class name, like this:
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
you're providing more information to the compiler, using a type cast to tell it "Hey, don't worry, I know for certain that the object I'll get back is a HomeViewController. Since you seem to have several types of view controllers that all have this method, the best thing to do is to declare the updateInfo method in a protocol and then have each of those UIViewController subclasses implement that protocol. So, your protocol declaration would be in a header file and might look like:
#protocol SomeProtocol
- (void)updateInfo
#end
and each class that has an -updateInfo method would just need to declare that it adopts the protocol:
#interface HomeViewController <SomeProtocol>
//...
#end
and then make sure that you have an -updateInfo in your class implementation:
#implementation HomeViewController
- (void)updateInfo {
//...
}
//...
#end
Then, in your code, you can either check that the object conforms to the protocol using -conformsToProtocol: like this:
if ([controller conformsToProtocol:#protocol(SomeProtocol)]) {
UIViewController<SomeProtocol> *c = (UIViewController<SomeProtocol>*)controller;
[c updateInfo];
}
or else just check that the object responds to the selector before calling it:
if ([controller respondsToSelector:#selector(updateInfo)]) {
[controller performSelector(updateInfo)];
}
The other answers you've received (using id or creating a common base class) are also good ones, but to be safe make sure you do some checking before calling your method. For example, you can use -isKindOfClass to make sure that the view controller you get back is in fact an instance of your common base class, and you can use -respondsToSelector: as above to check that an id points to an object that implements updateInfo.
In my project I have 3 controllers;
NavigationController
ServiceTableViewController
DateTableViewController
The ServiceTableViewController is the initial view controller. It has several rows which prompt the user to enter in data, which will be emailed to a particular email address. One of the rows, when tapped, sends the user to the DateTableViewController which prompts the user to select a date from the UIDatePicker.
The issue I am facing is getting data back from DateTableViewController in order to display a label on the ServiceTableViewController to show the date the user selects in the DateTableViewController. I know how to get information from one view controller to another, but to go in reverse, so to speak, is not something I know how to do. Any help is appreciated.
Take a look at this:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/CommunicateWithObjects.html
There are couple of ways to pass data back and forth between view controllers.
Delegates
Target-Aciton
Notification
KVO
but honestly delegates are really all you need really and it sounds like in your current case.
see this -> (Passing Data between View Controllers)
Having said that, if you use delegates, here is how ---
setup a protocol in DateTableViewController.h at the top like so:
#protocol DateTableViewControllerDelegate <NSObject>
- (void)userSelectedThisDate:(NSDate *)d;
end
put this with the other properties
#property (nonatomic, weak) id <DateTableViewControllerDelegate> delegate;
and in DateTableViewController.m with the date to send back
[self.delegate userSelectedThisDate:withTheDateToSendBack];
in and ServiceTableViewController.h add
#import "DateTableViewController.h"
#interface ServiceTableViewController : UIViewController <DateTableViewControllerDelegate>
and since you are UINavigationController, somewhere in ServiceTableViewController.m add this when you are about to push to the DateTableViewController
DateTableViewController *vc = [[DateTableViewController alloc] init];
self.delegate = self;
[self.navigationController pushViewController:vc animated:YES];
and finally put the delegate method in ServiceTableViewController.m
- (void)userSelectedThisDate:(NSDate *)d {
NSLog(#"%#", d); // this should show the returned date
}
Research delegate pattern (here) (a heavily used pattern within Apple frameworks). You want to define a delegate protocol which allows to a date to be passed to the delegate.
You could implement the pattern as an #protocol with a single method and a property on the DateTableViewController. The ServiceTableViewController sets itself as the delegate before pushing the DateTableViewController.
Or, you could implement using a block. Again, the ServiceTableViewController sets the block before pushing the DateTableViewController.