I have a RootViewController class and a UserSettingsController class. I have defined the UITableView methods (numberOfRowsInSection, cellForRowAtIndexPath) in the RootViewController class.
I want to reload the table view defined in the RootViewController class from the UserSettingsController class. How can I get control of the tableView Object in the RootViewController class from the UserSettingsController class?
I tried the following, but it tries to load a new tableview object.
RootViewController *rootViewController = [[RootViewController alloc]init];
[rootViewController.mytableView reloadData];
[rootViewController autorelease];
You can reload rootViewController.mytableView in viewWillAppear method of RootViewController itself. This will make rootViewController.mytableView reload when you are about to go to the rootViewController view. If the data you want to load is not much (as in takes more time to load like fetching data from the web) you will be fine with this solution.
Otherwise, to load rootViewController.mytableView from you settings view, you can use NSNotification like this:
In RootViewController.m :
//This goes in viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadTableViewData) name:#"ReloadRootViewControllerTable" object:nil];
Then make a method like this:
-(void) reloadTableViewData{
[mytableView reloadData];
}
In Settings view, where you want to reload the RootViewController tableView, write this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadRootViewControllerTable" object:nil];
This will automatically call the reloadTableViewData method of RootViewController without your need to do subclassing or anything. :)
Make use of notifications with custom name to call static methods in other classes. They are very handy.
I am presuming if what you want to do is to show the same exact table in two different view controllers.
Make myTableView a global or singleton and share it as a property between rootViewController and userSettingsController. Better yet, do a little more work and create a class for your myTableView so you can more easily set it up and manipulate it without having to spread duplicate code in the view controllers' implementation.
In each view controller, test to see if (myTableVIew == nil), and if so, go ahead and initialize and set it up (preferably by going through the tableview class you made). Once set up, you need to make sure you add myTableView to the properties (retained) of both view controllers. Then to display this tableView in each of the controllers, you will just have to do a [self.view addSubview:myTableView]; where self is the view controller that is active at the time.
Yes mahboudz you are absolutely correctly saying that tableview valuw ould be nil
Because if reinitialize the class from another class using init , the objects are reset to nil which are previously set.
And than when you want to reload it it doesn't works because the value of tableview is nil
I didnot get better answer for resolving this please let me know if any buddy knows the solution
Related
I'd like to go back to my root view from within a class method of view 1. When in an instance method of view 1, i would just say
[self.navigationController popViewControllerAnimated:YES];
but since self doesn't apply in a class method, I am wondering how to accomplish this. Pertaining to the illustration below, I am currently in a class method of View1Controller.m and I'd like to get back to Rootview. Thanks.
You can declare another method:
-(void)closeThisViewController
{
[self.navigationController popViewControllerAnimated:YES];
}
Then use NotificationCenter:
[[NSNotificationCenter defaultCenter] postNotificationName:#"notif_closeThisVC" selector:#selector(closeThisViewController) object:nil];
Although as jonkroll said, you're dealing with view controller stuff, we don't understand why you would put view controller related code inside a class method.
Edit
Sorry bad code above.
I meant to say you can use NSNotificationCenter to post a notification:
-(void)postNotificationName:(NSString *)notificationName object:(id)notificationSender
Then in the same view controller declare a NSNotificationCenter observer:
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
My brain was quicker than my fingers, so I kinda combined the two into one when I tried to explain the solution :P
It should more like this:
// posting a notification with NSNotificationCenter
[[NSNotificationCenter defaultCenter] postNotificationName:#"notif_closeThisVC" object:nil];
In your viewDidLoad method somewhere (I recommend at the top), add this:
-(void)viewDidLoad
{
// adding an observer with NSNotificationCenter
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(closeThisViewController) name:#"notif_closeThisVC" object:nil];
...
}
Hope that helps.
If you don't want to refactor this from a class method to an instance method (and there are certainly cases where you wouldn't want to do that), I'd suggest you add a completion block parameter to your class method:
+ (void)doSomethingWithCompletion:(void(^)())completion {
/* Do your thing... */
completion();
}
- (IBAction)doSomething:(id)sender {
[self.class doSomethingWithCompletion:^{
[self.navigationController popViewControllerAnimated:YES];
}];
}
This would allow you to cleanly separate the instance-less operation the class method performs from instance-specific dismissing of the view controller. You could also make the completion block accept an error object if the operation can fail.
You could do something similar with a delegate object or even by passing in the view controller to dismiss, but this design seems to offer the cleanest separation with the most modern feel.
There are going to be legitimate arguments that encourage you to refactor so that you do have access to the current view controller and can access the navigation controller via currentVC.navigationController. Remember, it can still be a class method, just give it an extra argument when you call it (or start the call chain that calls it) from the VC.
However, I also had to solve this in one of my apps, so I just made sure that the navigation controller was globally accessible to everyone, always via pointer ("weak ref")
If you declare a global variable like this (say, in "Navigation.h")
extern UINavigationController *gNavController;
and define it in your AppDelegate.m (pays to review distinction between declaration/definition if you're rusty on that):
UINavigationController* gNavController;
and then assign it when you start up in application:didFinishLaunchingWithOptions::
(assuming the delegate has a property called viewController that is your navigation controller):
gNavController = viewController;
Then as long as you #import Navigation.h, you'll always have access to the navigation controller. This also makes getting a handle to its view for popups/popovers much simpler.
This also assumes your nav controller is never released for the lifetime of the app (probably true unless you're doing something unusual).
In a UIViewController subclass, I have the following methods:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// do something
myTextField.text = #"Default";
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// do something
[myTextField selectAll:self];
[myTextField becomeFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
// do something
myTextField.delegate = self;
}
The NIB has been created using Interface Builder. The corresponding view controller object is pushed by the navigation controller through pushViewController.
The inteded behavior is to show a default text entry in a text field, to select the entire text and to set the text field as first responder. [Edit: I've noticed that selecting all and making first responder makes no sense as the selection would dissapear; still, I wonder why the methods behave as described next.]
However, while methods viewDidLoad and viewWillAppear are called, the method viewDidAppear is not called. Can anybody tell me why? Most questions I found on the web and here deal with both viewWillAppear/viewDidAppear are not working; I also understood that in subviews or programmatically created views these methods are not evoked; but this does not apply in case and also I wonder why one of these "lifecycle" methods is evoked and the other not.
Any idea? Thanks!
I had this issue happen to me: viewWillAppear was being called but viewDidAppear was not!
I finally figured out that this was because I had a tabBarController where I overloaded it's own viewDidAppear and forgot the [super viewDidAppear:animated];
It threw off every VC in every tab! adding that line back in fixed it for my other VC's.
Hope this helps someone!
There is one case when viewWillAppear will be called but viewDidAppear will not.
Suppose you have two viewControllers and you push from the first to the second controller. Then, using the swipe, you want to go back to the first one, both controllers are displayed at the same time and at that moment viewWillAppear will be called from the first controller.
This way, you do not finish the swipe and stay on the second controller and viewDidAppear will not be called from the first controller.
I had the same problem.
I had copy/pasted viewDidAppear to create viewWillAppear but had forgotten to change the super.viewDidAppear() call. This then seemed to stop viewDidAppear from being called.
It sounds like somewhere in your code you have missed or messed-up a call to the superclass.
The call to viewDidAppear: should always follow viewWillAppear: unless you are doing something custom, which you say you don't. I don't know why it doesn't work but here are a few ideas:
Could it be that you are doing something strange in one of the delegate methods for UITextFieldDelegate? It's unlikely that it would affect viewDidAppear: being called but it could be a culprit.
Have you loaded a lot of stuff into memory before pushing the view? I'm not sure what would happen if you got a memory warning between viewWillAppear: and viewDidAppear:.
Have you tried to do a Clean? Sometimes that can help.
In cases like these when it should work I usually create a new class and the introduce the functionality one at a the time to see if I can get it work that way. I tried your code in a new Navigation Based project where I added a new UIViewController with an outlet to the text field. Then I pasted the code from the question and it did work as expected.
This can be because you added a child view controller to your parent VC in viewDidLoad or viewWillAppear. The child's appearance prevents the call to viewDidAppear.
This is a crazy thing to do, and I only know because this was a bug in my code. I meant to add the child VC to this VC, not the parent VC.
Is it safe to assume that an attribute, namely fetchedResultsController, of chatViewController, an instance of a subclass of UITableViewController, is always nil when viewDidLoad is called, assuming that it's set to nil in viewDidUnload? Phew!
If that's the case, then I see no immediate need to redefine the accessor function like in the Xcode example application CoreDataBooks. I'd rather just put all that code in viewDidLoad instead of in a separate function because that's the only place I'll use it.
viewDidLoad is called after your view is loaded. Whether or not fetchedResultsController is nil or not depends on how the viewController is initialized. For example, when creating the detailViewController, you could set its fetchedViewController before viewDidLoad is called:
RecipeDetailViewController *detailViewController = [[RecipeDetailViewController alloc] initWithStyle:UITableViewStyleGrouped];
detailViewController.fetchedResultsController = fetchedResultsController;
[self.navigationController pushViewController:detailViewController animated:animated];
[detailViewController release];
That said, then nil'ing fetchedResultsController in viewDidUnload would ensure that it's nil.
ViewDidLoad Called in These Secnarion:-
1.when we push the view controller after creating it’s object by segue or by stoary board id.
2.it called more than one in the case of creating instance more time in application and push it again and again.for example:-if you implement like coaursal(that having required to additional controller during scrolling) like that it’s need so it can called multiple times viewDidLoad.
3.it called when all memory instance (uiviewcontroller and it’s subclass instantiated) that means when our view is ready to load in memory with the address.
4.Remember only child class controller object is created..parent class object never been instantiated during normal Secnarion.
You have to assume that viewDidLoad can be called multiple times. If there is a memory warning sent, your view controller will unload the view from memory, and the next time it is needed viewDidLoad will be called.
viewDidLoad is called only when view is instantiated for first time . If you're not recreating the view controller each time in your application, you'll only get it called once (and called again if you get a memory warning, and the view is nil'd out).
Okay, so I'm working on an iPhone app with a preferences-esque section where there's a base TableView, which has one section, and several rows of customized cells to hold a name and a value label. Clicking on a row brings up another View, which allows the user to pick from a list (another TableView), and select an item.
All of these TableViews are done programatically. The base TableView has a property that holds a Controller instance for each of the "pick from the list" Views. Each of the "pick from the list" views has a property called chosenValue that has the currently-selected option. I've gotten the UI to handle the didSelectRowAtIndexPath to update the chosenValue property and then "pop" the view (going back to the main TableView). But, even though the main TableView's cellForRowAtIndexPath method references the chosenValue property of the subview that's held in a property, the view doesn't update when an item is selected. In short, how can the sub-view trigger a reloadData on the parent object, after it "pops" and unloads?
You could realize this using the notifications or delegation, for example.
Using notifications your first view controller has to register for a notification like this:
…
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(methodToBeCalled:)
name:#"myNotification"
object:nil];
…
- (void)methodToBeCalled:(NSNotification *)notification {
[self.tableView reloadData];
// do something else
}
Then you can raise a notification in your second view controller like this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:nil];
Using delegation you could implement a property for a delegate on the second view, set that to self before pushing the second view controller and call a method on the delegate when needed.
You should define a delegate for that.
In the child TableView define a protocol:
#protocol ChildViewControllerDelegate
- (void) somethingUpdated;
#end
Also define a property for a delegate implementing this protocol:
id <ChildViewControllerDelegate> delegate;
Then in Parent all you need to do is to define a method somethingUpdated, assign itself (parent) to a delegate property in Child and call this method in Child view when you need to update it. Implementation of somethingUpdated in Parent can be different based on what you're trying to accomplish.
flohei thanks so much for posting this!! I got it working with NSNotificationCenter! I have a child view calling a method in a parent view now! Awesome. #sha I tried your protocol way also but could not figure it out. Perhaps a more illaborate discription would help a noob like me. I have been reading that your protocol way is the better cause I guess less code? So i'll try to keep learning it. Thanks to you both!
I have an view controller class. I want to do some things in my view when the accelerometer detects movement and calls the accelerometer:didAccelerate: method in the delegate object.
That delegate object is the problem here in my brain. Currently, my brain is freezed and I don't get it what would be better. Let me know what you think!
Solution 1)
In my view controller class I conform to the UIAccelerometerDelegate protocol, and implement that accelerometer:didAccelerate: method.
In the -applicationDidFinishLaunching: method of my AppDelegate class I set that view controller object up as the delegate for receiving method calls upon accelerations. I think that's not really good.
Solution 2)
I create a blank new object called AccelerationDelegate, conform to that UIAccelerometerDelegate protocol, implement that accelerometer:didAccelerate: method and in the -applicationDidFinishLaunching: method of my AppDelegate class I set that view controller object up as the delegate for receiving method calls upon accelerations.
But for solution 2 my brain got stuck a little bit! How would I access the view objects from my view controller inside that object?
The problem here is, that I have more than one view controller around. I use a tab bar controller to switch between them.
Any suggestions how I could get that right?
I agree that the second method is better. Are you looking to access just the currently selected tab view, or just a specific view in your app.
In any case, what I would do is to set up properties for your UITabViewController in your UIApplicationDelegate so that you can access it from the delegate (you can get the app delegate by calling [[UIApplication sharedApplication] delegate]). For example:
YourApplicationDelegate *appDelegate = (YourApplicationDelegate *)[[UIApplication sharedApplication] delegate];
FirstUIViewController *firstViewController = appDelegate.firstViewController;
[firstViewController doStuff];
where firstViewController is a property on your delegate.
If your acceleration is specific to one view controller, then it makes sense to have the view controller receive the information necessary to alter its own subviews. However, it might be better to set your view controller to be the delegate when the view appears, and set the delegate to null when it disappears. (Specifically, - (void) viewWillAppear: and - (void) viewWillDisappear:)