I have found a few questions regarding this topic but known seem to get my problem solved. I have an iPhone app that pulls and XML feed from a server, parses it, then displays the data in three UITableViews (sorting occurs at parsing). I currently have NSTimers to update the UITables once the data is done being parsed. I know there is a better way. I am trying to use the parser delegate method
In my Parser.m
-(void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog(#"done Parsing, now update UITable in otherViewController");
otherViewController *otherUpdatingtmp =[[otherViewController alloc] initWithNibName:#"otherViewController" bundle:nil];
[otherUpdatingtmp updateTable];
}
to trigger the updateTable method that is located on the otherViewController to reloadData in the table. My NSLog tells me my updateTable is firing in the otherViewController thread however I can not seem to get my TableView to update because its returned as NULL.
In my otherViewController.m
-(void)updateTable{
[tabeViewUI reloadData];
NSLog(#"update table fired. table view is %#.", tabeViewUI);
}
It's gotta be something small i've overlooked. Thanks in advance!
EDIT 7/24/12:
Thanks to #tc. for turning me onto using [NSNotificationCenter]
in my parser.m
-(void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog(#"done Parsing, now update UITable's with NSNotification");
[[NSNotificationCenter defaultCenter] postNotificationName:#"parserDidEndDocument" object:self];}
then in each of my UITableViews I added:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateTable) name:#"parserDidEndDocument" object:nil];
}
return self;
}
lastly:
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
I hope this helps someone else. It certainly helped me! Thanks #tc.
Related
I have the following snippet of code in viewDidLoad which I post a notification to be received by a different ViewController, and prior to iOS 7 (and XCode 5) this has worked:
if ([PFUser currentUser] && [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) {
NSLog(#"Current user exists and is logged in"); //current works, so I know that this if-statement is satisfied
[self performSegueWithIdentifier:#"GoToRatingsView" sender:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"testSetup" object:nil]; //part in question
}
And in the segued ViewController, I have the following:
(void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(testSetupFunction) name:#"testSetup" object:nil];
}
(void)testSetupFunction
{
NSLog(#"This function executed");
}
Currently, testSetupFunction does not executed, which means the notification is not received. I am unsure whether its because I have segued to a different view and then posted the notification, or that this is something new with iOS 7, but currently the notification is no longer received.
Thanks for your help!
Post the notification once the viewcontroller has time to load.
- (void)postNotificaiton
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"testSetup" object:nil];
}
if ([PFUser currentUser] && [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) {
[self performSegueWithIdentifier:#"GoToRatingsView" sender:self];
[self performSelector:#selector(postNotification) withObject:nil afterDelay:0.0];
}
You could also post a notification from your segued View Controller saying that it was loaded, and in response to that notification, post your testSetup notification.
did you try adding the observer in the init method
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(testSetupFunction) name:#"testSetup" object:nil];
// Custom initialization
}
return self;
}
OR You can also try to put a log in viewDidLoad to check actually the view is loaded at that time, sometimes it takes time to load the view, view is only loaded only when it is needed.
I have a view controller, when it's dissming with completion, I post a notfication, and in a subview which contained in another view controller, has added as a oberserve. But, when it tries to execute post notificaiton methode, exec_bad_access happend. what's wrong? The codes are:
BrandListByIdViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *bid = self.brands[indexPath.row][#"id"];
[self dismissViewControllerAnimated:YES completion:^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"SelectedBrandId" object:nil];
}];
}
SearchNewProduct.h
#interface SearchNewProduct : UIView
#end
SearchNewProduct.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didSelectedBrandId::) name:#"SelectedBrandId" object:nil];
}
}
- (void)didSelectedBrandId:(NSNotification *)notif{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
Even I get rid of the userInfo, still bad access. I created a similar situation in another new project, it works fine.
I didn't realize that you were dealing with a UIView and not a UIViewController (should have read your question better). I think what is happening is that the View is receiving notifications even after being released. Make sure to call dealloc in your UIView and remove itself as an observer:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Also, put an NSLog in that UIView's initWithFrame method to see if it is being called more than once.
This question is very similar:
ios notifications to "dead" objects
Not sure if this is the reason, but when you add your view to the notification center, your selector is wrong:
selector:#selector(didSelectedBrandId::)
There should only be one colon. The entire line should be:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didSelectedBrandId:) name:#"SelectedBrandId" object:nil];
Two colons indicates the method takes two arguments, but it only takes one.
I have a modalViewController that comes up over the top of a viewController with a tableView. When the user clicks a button on the modalViewController I want to reload the tableView within the viewController with this:
[tableView1 reloadData];
I do not want to put the reload in the viewDidAppear or viewWillAppear methods as they get called when i do not need the tableView to reload (i.e. when the user clicks the back button to return to the tableView).
Is there a way to do this?
Try
1) write one method which reloads the table data.
2) Call it on the back button clicked.
This is the classic delegate pattern problem, in your modal view controller you need a delegate reference to the current view controller presenting it
//Modal
#protocol ModalVCDelegate
- (void)tappedBackButton;
#end
#class ModalVC: UIViewController
#property id<ModalVCDelegate> delegate;
#end
#implementation
- (void)backButtonTapped:(id)sender
{
if (self.delegate)
[self.delegate tappedBackButton];
}
#end
Now, in your presenting VC, just process this delegate message
//Parent VC
- (void)showModal
{
ModalVC *vc = [ModalVC new];
vc.delegate = self;
//push
}
- (void)tappedBackButton
{
[self.tableView reloadData];
//close modal
}
You can use delegate . If find it more harder then alternative is to use NSNotificationCenter. You can see accepted answer for Refreshing TableView. This is really very short, easy and understandable way.
using Notification like bellow Method:-
Create NSNotificationCenter at yourViewController's ViewdidLoad Mehod
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(ReloadDataFunction:)
name:#"refresh"
object:nil];
[super viewDidLoad];
}
-(void)ReloadDataFunction:(NSNotification *)notification {
[yourTableView reloadData];
}
Now you can Call this Notification from your modelViewController BackButton or else you want from calling this Refresh notification like putting this line of code:-
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh" object:self];
NOTE: postNotificationName:#"refresh" this is a key of particular Notification
Try to use this one
Make a Button and click on this button and than you can reload your data.
This button make custom and use it on background.
- (IBAction)reloadData:(id)sender
{
[tblView reloadData];
}
You can use NSNotification to refresh table on ViewController.
Inside viewController :
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
Write code in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadMainTable:)
name:#"ReloadTable"
object:nil];
- (void) reloadMainTable:(NSNotification *) notification
{
[tableView reload];
}
Inside ModelViewController:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ReloadTable"
object:nil];
Here you can also send custom object instead of nil parameter. But be care full about removal of NSNotification observer.
On Facebook's iPhone app, the news feed refreshes every time the app becomes active. I would like to do something similar, but I'm concerned about a race condition. The general bootstrapping of my app is as follows:
UIApplicationDelegate
- (void)applicationDidFinishLaunching:(UIApplication*)application
{
[window addSubview:[self.navigationController view];
[window makeKeyAndVisible];
}
- (void)applicationDidBecomeActive:(UIApplication*)application
{
[rootViewController refresh];
}
RootViewController
#pragma mark custom
- (void)refresh
{
if (self.newsFeedModel == nil) {
self.newsFeedModel = [[NewsFeedModel alloc] initWithDelegate:self];
}
[self.newsFeedModel request];
}
#pragma mark UIViewController
- (void)viewDidLoad
{
// initialize the table
// add subviews and whatnot
}
#pragma mark NewsFeedDelegate
- (void)newsFeedSucceeded:(NSMutableArray*)feed
{
// reload table view with new feed data
}
After sprinkling NSLog everywhere, I determined the order of operations to be:
applicationDidFinishLaunching
applicationDidBecomeActive
refresh
viewDidLoad
newsFeedSucceeded
Notice how refresh is called before the root view has been loaded. While we're busy querying the server, the root view loads. When the server responds, the root view is populated with the feed. This works in most cases because the network operation takes a long time. However, if the network operation finishes faster than view can be loaded, then I will be attempting to construct the news feed before the view has been loaded. This would be bad. What is the best Cocoa Touch practice for solving this race condition? I would just set a bunch of flags to determine what state we're in and refresh the news feed depending on the state, but I'm wondering if there were built in events in Cocoa Touch to handle this for me.
I think you want to take a look at applicationWillEnterForeground: instead.
applicationDidBecomeActive: can be called while your app is still running in the foreground. For instance if a text message comes while your app is in the foreground and the user dismisses it, applicationDidBecomeActive: will get called.
You can subscribe to the UIApplicationWillEnterForegroundNotification event in your RootViewController using NSNotificationCenter. I would do this in RootViewController initWithNibName: or whichever init method you are using.
Now you just need to call refresh in 2 places. Once at the end of viewDidLoad and again whenever applicationWillEnterForeground: is called.
This should solve your race condition problem. Since RootViewController is handling it's own refreshing when it knows it is ok to do so.
RootViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
return self;
}
- (void)viewDidLoad
{
// initialize the table
// add subviews and whatnot
[self refresh];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self refresh];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
I'm using AsyncSocket to connect to a server from my iPhone App. In the delegate that received data from the server, I post a notification that would tell the tableView's delegate to trigger a reloadData on the tableView:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag {
[[NSNotificationCenter defaultCenter] postNotificationName:#"PEERSTATUSCHANGED" object:self];
[sock readDataToData:[AsyncSocket CRLFData] withTimeout:-1 tag:0];
}
and on the viewController:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(peerStatusDidChange:) name:#"PEERSTATUSCHANGED" object:nil];
}
return self;
}
- (void)peerStatusDidChange:(NSNotification *)notification {
NSLog(#"NOTIFICATION RECEIVED");
}
Now, this doesn't work at all. The notification is posed but not recognized by the ViewController. However, when I do the same thing in applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
protocol = [[XBBProtocol alloc] init];
SourceListViewController *sourceListVC = [[[SourceListViewController alloc] initWithNibName:#"SourceListViewController" bundle:nil] autorelease];
UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:sourceListVC] autorelease];
[[NSNotificationCenter defaultCenter] postNotificationName:#"PEERSTATUSCHANGED" object:self];
[protocol connectToServer];
// Override point for customization after application launch
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
I got the notification received in viewController.
Anyone knows why? does it have something to do with delegate methods of AsyncSocket being in different thread?
Thanks in advance.
One possibility is that your initWithNibName:bundle: method is not actually being called. If you instantiate the view controller in a NIB (rather than in code), then it calls initWithCoder: instead.
A quick way to check is to put a breakpoint in initWithNibName:bundle:.
Try putting the method that sends the notification in a different method, and call it with "performSelectorOnMainThread". It's very likely your network code is getting called in a background thread and thus when the notification fires, it informs the table view on the same thread...
You can't make UI calls on anything but the main thread.