NSTableView reload data from different controller - objective-c

I am having problems updating the content of two tables, which are placed in two different tabs of a NSTabView and each controlled by a different controller (Controller1 and Controller3), from a third controller (Controller2).
I tried three different approaches:
Approach #1:
I created in Controller1 (the controller of the table invitesTableView) the method refreshTable :
- (void)refreshTable {
invites = //fill my mutable array
NSLog(#"%#",invites);
[self.invitesTableView reloadData];
NSLog(#"invite's table view updated");
}
which I later call from Controller2 doing :
Controller1 *controller1 = [[Controller1 alloc] init];
[controller1 refreshTable];
NSLog prints the content of the array invites correctly, but the table is not updated. I should say that, at application launch, I call the very same method in Controller1 and the content of the array invites is correctly loaded in the table.
Approach #2: When in Controller2 I do:
Controller1 *controller1 = [[Controller1 alloc] init];
controller1.invites = //fill my mutable array
NSLog(#"%#",controller1.invites);
[controller1.invitesTableView reloadData];
But again the content of the table is not updated, even if the NSLog shows the correct content.
Approach #3: As Controller1 controls the content of a NSTabViewItem, I integrated in Controller1 the method:
- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)inviteTab
and it works just fine, because I call the method refreshTable as soon as open the tab controlled by Controller1. The problem is that I have different tables in different tabs but I can set only one controller as the delegate of the NSTabView, so I can not update the table controlled by Controller3 in a different table.
I don't think there is an easy solution to approach #3, apart from merging Controller1 and Controller3, but I think I am just missing something with respect to the first two approaches.
Hope I explained myself clearly. Can anyone help? Thanks in advance!

You can use notifications/observer to reload tableview.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadTable:)
name:#"reloadNotification"
object:nil];
- (void)reloadTable:(NSNotification *)notif {
[self.tableName reloadData];
}
In your first tab use this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadNotification"
object:nil];

Related

Objective-C: Pass data between two not connected View Controllers

I have a ViewController A with a UILabel and a ViewController B with a button, so I want to update the label once I press the button. I could use delegates for this, but my ViewControllers are not connected and I can't use something like setDelegate, I don't create any instance of one in another. So basically they are created somewhere else. Is there any way to do that?
Variant 1: if ControllerA and ControllerB life-time differs
Use NSUserDefaults. On ControllerB button click store data into NSUserDefaults, in ControllerA read data from NSUserDefaults and show in label (NSUserDefaults is also observable, so can track changes in run-time)
Variant 2: if ControllerA and ControllerB both currently in run-time
Use NSNotificationCenter. On ControllerB button click post a NSNotification with data in userInfo, and ControllerA in notification handler extracts data from userInfo and assign to label.
Use Coordinator pattern.
Class that will create/get the 2 instances of both VCs,
and with delegates will move the data between them.
The advantage of Coordinator pattern is that your VCs can be re-used on others places (same or other project), and also the code is cleaner.
To expand on Apsperi's answer:
Notification method of changing the view controller:
In the view controller with the button on button press call:
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh"
object:self userInfo:nil];
Then in the viewdidload or viewdidappear method of the label view controller place the "receiver" for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mymethotochangetext) name:#"refresh" object:nil];
The NSUserDefault method:
In the view controller with the button save a NSUserDefualt:
[[NSUserDefaults standardUserDefaults] setObject:somestring
forKey:#"myuniquekeyname"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then in the view controller with the label you can get the saved string when the view loads and place it in the text field:
self.label.text=[[NSUserDefaults standardUserDefaults]
stringForKey:#"myuniquekeyname"];

Presenting correct View Controller instance depending on cell selection in UITableView

I understand this might be covered in parts in answers to other questions (see references) I've seen on the site, but due to my limited experience I haven't been able to understand each part as it relates to my code. Please forgive me for any duplications.
I have a PFQueryTableViewController (essentially a UITableViewController) called threadsViewController that sources cell information from a Parse backend. The table view consists of threads similar to what you would see on a web forum.
I then have a separate class postsViewController which is another PFQueryTableViewController that I wish to display a table of all the responses ('posts') to that particular thread.
The functionality I'm looking for is for a user tapping on a thread (left screen in the image) to be presented with a postsViewController (right screen) containing only those posts/responses related to that thread. (See basic diagram below).
What I do know from my research:
The Parse backend is established with classes for Thread and Post.
I'm probably going to use the didSelectRowAtIndexPath delegate method
What I need help with:
How can I know which thread cell a user tapped on, and then pass that on so that the postsViewController only displays posts from that Thread?
A layman's description of how to use indexPath.row etc
I understand how to complete PFQueries etc to get the data for the cells, I just don't know how to implement the navigation and how to tell postsViewController which posts to show.
In case it helps somehow, here is my implementation so far. I have tried addign a property to the postsViewController called fromThread to somehow store the thread but apart from that I'm out of ideas!
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
postsViewController *postsVC = [[postsViewController alloc] init];
postsVC.fromThread = //?
[self performSegueWithIdentifier:#"threadsToPosts" sender:self];
}
References
How to filter a Parse query by an tableview index?
Pass Index Number between UITableView List segue
didSelectRowAtIndexPath and prepareForSegue implementation
You're on the right track. You need know how to do two things: (1) access your parse objects by indexPath, and (2) push a new view controller in a navigation controller.
(1) is simpler: PFQueryTableVC provides a method called objectAtIndexPath: that does just what you need.
// indexPath is the indexPath parameter to the didSelectRow delegate method
PFObject *fromThread = [self objectAtIndexPath:indexPath];
(2) is simple, too. But more complicated because there are a couple ways to do it. Segue is the more modern way, but I think the old way is simpler, and certainly easier to describe in code. View Controllers are given storyboard ids on the "Identity" tab in the storyboard editor. Give your Posts-presenting VC a storyboard id like "PostVC".
To get a new instance, use that storyboard id as follows:
MyPostVC *postVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PostVC"];
// initialize it with the PFObject we got above
postVC.fromThread = fromThread;
// present it on the navigation stack
[self.navigationController pushViewController:postVC animated:YES];
And fromThread is just what the PostVC will need to form a query for posts associated with the selected thread.
You can pass data of cell to next ViewController in prepareForSegue with something like this
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"threadsToPosts"]) {
UINavigationController *navCon = segue.destinationViewController;
postsViewController *postsViewController = [navCon.viewControllers objectAtIndex:0];
// Whatever you are populating your tableView with
Thread *thread = [self.thread objectAtIndex:self.tableView.indexPathForSelectedRow.row];
postsViewController.thread = thread;
}
}

UIViewcontroller not reloading after notification from observer

Hi I have a uiviewcontroller that adds 4 childviews after receiving data from a webservice. I add an observer to the uiviewcontroller that watches when the data is completely loaded from the webservice. Once it is, the uiviewcontroler adds 4 childviews.
however my problem is it doesn't do this unless i switch to another tab view than go back to that tab again.
Is there a way to reload this page? I used [uiviewcontroller.view setNeedsDisplay] under the function that responds to the notification from the observer but the view doesn't refresh.
thanks for your guys' help :)
here is my observer function:
-(void)projectListFromServer:(NSNotification *) response
{
NSLog(#"%#",response);
for (int i = 0; i < 4; i++)
{
childview2 = [self.storyboard instantiateViewControllerWithIdentifier:(#"slider")];
childview2.image = #"wicked";
childview2.couponID = #"cool";
NSLog(#"%#", self.childview2.couponID);
[self addChildViewController:childview2];
}
[self.childview.view setNeedsDisplay];
}
You are calling addChildViewController, but then it looks like you aren't actually adding childview2's view hierarchy to this containing view controller's view hierarchy.
You'll need to do something like this, adjusted of course depending on where in your current hierarchy you actually want these new views to be inserted:
[self addChildViewController:self.childview2]; // assuming childview2 is a #property
[self.view addSubview:self.childview2.view]; // adjust based on your actual hierarchy
[self.childview2 didMoveToParentViewController:self];
BTW, you should avoid giving view controllers names like childview2. Views and view controllers are two completely different things.

Sharing data across ViewControllers

After reading a bunch of stackoverflow posts and forums I got really confused on this subject. Alot speak about using delegates, but can result in memory problem if there is a big amount of data.
I am only to handle around 600-bytes of data from a TCP/IP device and override the existing value with the new.
I have made a RootViewController which has a special view and the TCP/IP socket, furthermore I have two other ViewControllers which has to use data from the RootViewController (TCP/IP).
Should I perhaps make a seperate class for the TCP stuff instead of mixing it up in my RootViewController?
Also how should I handle the sharing of data across the ViewControllers?
Thanks in advance.
If you are doing a lot with your TCP 'stuff' (for example get the data, display the data, reformat the data, compair or change the data and then re-save etc) then I would suggest creating a separate class (probably NSObject) that has all the metods to handle the TCP stuff and then create an instance of that object in your root view controller.
As for getting the data in other view controllers you would have to create a reference to your root view controller in other view controllers and then connect them in interface builder (or programmatically). Just make sure you dont create a brand new instance of root view controller (i.e. dont use [[RootViewController alloc] init];)
Does that make sense? If you post more about the structure of your app (like are you using a navigation controller etc) I could be more specific for you.
Good luck!
For sharing data among classes you can use notifications mechanism. Post a notification in your FromClass.m:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:yourTCPData, #"TCPData", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"gotTCPData" object:nil userInfo:options];
In your ToClass.m viewDidLoad add this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(makeSomeThingUsefulWithTCPData:)name:#"gotTCPData" object:nil];
...and somewhere in your ToClass.m add this method:
- (void)makeSomeThingUsefulWithTCPData:(NSNotification *)notification {
NSDictionary *dict = [notification userInfo];
yourTCPDataClass *yourTCPDataObject = [dict objectForKey:#"TCPData"];
// Here make something useful with your TCP data
}
Also add this in your ToClass.m viewDidUnload to release observer object when view will be unloaded:
[[NSNotificationCenter defaultCenter] removeObserver:self];

Exchange data between un-related (table)viewcontrollers?

I have a app with TabBar, under the main tabbar there are navigation controllers, under these there are 'branches' of tableviewcontollers.
How can I make two unrelated, non segue-connected, under two different tab icons viewcontrollers exchange information between them?
Tried with delegating but I can't get the instance variable to the delegator from the delegatee (there's no relation between them, no segue.destinationviewcontroller etc)
any ideas?
practical:
the app shows list of subitems (the parent tableview has the items), andon the other tab the recent items that were selected are getting added (but maxiumum 10 and sorted by most recent).. been breaking my ** on it...
thanks
I find NSNotifications not the best way to do this since you do couple the unrelated TableViewControllers. I think the flow of information in an application is crucial.
The solution I would personally favor is having a central class, that manages the global data for your application. This can be the AppDelegate class or an arbitrary manager class that manages the flow of data and "pulls the strings" and mediates between the different independent ViewControllers.
Example:
Using this code you can get hold of your UITabBarController and set your class as the delegate of your ViewControllers etc.:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *rootTabBarController = (UITabBarController *)self.window.rootViewController;
MyFirstTableViewController *firstVC = [rootTabBarController.viewControllers objectAtIndex:0];
firstVC.delegate = self; // Get informed about events in the first ViewController
MySecondTableViewController *secondVC = [rootTabBarController.viewControllers lastObject]; // Assuming you only have two Tabs
// Once you have the rootTabBarController you can cast it to the corresponding ViewController and access any nested UIViewControllers
return YES;
}
#pragma mark - MyFirstTableViewControllerDelegate
-(void)firstTableViewController:(MyFirstTableViewController *)sender didSomethingFancy:(MyFancyObject *)fancy{
// Do stuff like fetching some data, based on the event from the first ViewController
// Maybe tell the secondVC to refresh its data etc. etc.
}
Have you considered posting an NSNotification, passing the data you want to transfer in userInfo?
From the Apple documentation:
The object sending (or posting) the notification doesn’t have to know
what those observers are. Notification is thus a powerful mechanism
for attaining coordination and cohesion in a program. It reduces the
need for strong dependencies between objects in a program (such
dependencies would reduce the reusability of those objects).
The class sending a notification does it as follows:
[[NSNotificationCenter defaultCenter] postNotificationName: <notification-name>
object: self
userInfo: <your-user-info>];
and the class listening for the notification registers for notifications using:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(someMethod:)
name: <notification-name>
object: nil];
and has a method:
- (void) someMethod: (NSNotification *) notification
{
NSDictionary * myInfo = notification.userInfo;
//do something with myInfo
}
You can use #property and #synthesize to create default getters and setters. You can aso create your own getters and setters manually.