I have two view controllers (BuildingsViewController and RoomsViewController) that both use a function within the App Delegate called upload. The upload function basically does an HTTP request, and if its successful or unsuccessful, triggers a uialertview. This is working fine.
The part I'm struggling with is from within the app delegate's connectionDidFinishLoading method. I need to be able to basically refresh the current view controller via perhaps viewWillAppear method of that view controller. Inside the viewWillAppear function of each view controller I have code which determines the buttons on the bottom toolbar.
I want the "upload" button in the toolbar of each view controller to automatically be removed when the uploading is done via the app delegate.
I've tried doing [viewController viewWillAppear:YES] from within the connectionDidFinishLoading method of the app delegate, but it never gets called.
I hope I'm clear enough. Any help is greatly appreciated.
Thanks.
To do the refresh of the view do not call viewWillAppear if the view is already displayed. What you want to do is the following:
When ConnectionDidFinishLoading method is triggered post a notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"refreshView" object:nil];
In your viewController observe for this notification. You do it by adding this code to your init or viewDidLoad method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshView:) name:#"refreshView" object:nil];
Now implement -(void)refreshView:(NSNotification *) notification method in your viewController to manage your view to your liking.
If you are targeting iOS 4.0 and later, you can use the window's rootViewController property to get the current view controller.
[window.rootViewController viewWillAppear];
If you want your application to run on versions prior to iOS 4.0, then you could add an instance variable to the application delegate to remember which view controller called the upload method, having the controller send itself as a parameter.
- (void)upload:(UIViewController *)viewController {
self.uploadingViewController = viewController; // This is the property you add
...
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.uploadingViewController viewWillAppear];
self.uploadingViewController = nil;
}
You should also consider using a different method to reload the buttons, something like reloadButtons, since it is not related to the view appearing in this case. You would then call that method from within viewWillAppear.
Step 1:
In your App Delegate .h file you need to declare a protocol like so:
#protocol AppConnectionDelegate <NSObject>
#required
-(void)connectionFinished:(NSObject*)outObject;
#end
In the same file, add an ivar like so:
id *delegate;
Declare the ivar as a property:
#property (nonatomic, assign) id<AppConnectionDelegate> delegate;
In the App Delegate .m file, synthesize the ivar:
#synthesize delegate;
In the App Delegate .m file, on connectionDidFinishLoading do:
if([self.delegate respondsToSelector:#selector(connectionFinished:)])
{
[self.delegate connectionFinished:objectYouWantToSend];
}
In your viewcontroller's .h file, implement the AppConnectionDelegate by importing a reference to the app delegate file:
#import "AppDelegate_iPhone.h" //if using iPhone
#import "AppDelegate_iPad.h" //if using iPad
In the same file, at the end of the first line of the interface declaration do:
#interface AppDelegate_iPhone : AppDelegate_Shared <AppConnectionDelegate>
Declare ivars accordingly:
AppDelegate_iPhone *appDelegate; //if using iPhone
AppDelegate_iPad *appDelegate; // if using iPad
In your viewcontroller's .m file in the viewDidLoad(), get a reference to your app delegate using:
If iPhone;
appDelegate = (AppDelegate_iPhone*)[[UIApplication sharedApplication] delegate];
If iPad:
appDelegate = (AppDelegate_iPad*)[[UIApplication sharedApplication] delegate];
Then set the viewcontroller to be the delegate in viewDidLoad() by doing:
appDelegate.delegate = self;
Now you need to simply implement the connectionFinished method in the .m file:
- (void)connectionFinished:(NSObject*)incomingObject
{
//Do whatever you want here when the connection is finished. IncomingObject is the object that the app delegate sent.
}
Now whenever your app delegate's connectionDidFinishLoading is called, the view controller will be notified.
[It's a best practice to set appDelegate.delegate = nil if you're done using the connectionFinished callback]
This is tried and tested. If you have questions, leave a comment......
--EDIT--This is a robust alternative to NSNotification. I use both depending on the requirements. The process I use to decide between using NSNotification or a delegate callback using a protocol is simply:
For notifications:
One sender, multiple listeners.
No reference possible between sender and listener.
Complex/multiple objects need not be sent
For delegate callbacks using protocols:
One sender, limited (usually 1) listeners.
A reference between sender and listener is possible.
Complex/multiple objects are to be sent (for example, response objects that need to be sent)
I know sending objects is possible through notifications but I prefer protocols for that.
--EDIT--
Worse comes to worst, you can have both view controllers adhere to a simple one method protocol that will remove that button and refresh the view. Then in your connectionDidFinishLoading method, since you know your view controller must adhere to that protocol, by your design, you simply do something like
ViewController<MyProtocol> curView = (Get the current view controller somehow);
[curview refreshView];
Related
I need to transfer NSURL data to target UIViewController for UIWebView. How can I do it to load required url from ViewDidLoad of target view? I am using storyboard, ARC, iOS5.
You could use a delegate to send the URL from UIViewController to UIWebView.
In your webview create a delegate property:
#property(nonatomic,weak) (id) delegate;
Synthezize as normal. Then in the webviews viewDidLoad method write:
self.someWebViewURLProperty = self.delegate.someURLPropertyFromUIController;
Now set the delegate in your ViewController. In your UIViewController in the viewDidLoad method write:
self.webViewProperty.delegate = self;
It may seem a bit more involved than using prepareForSegue, but both methods are valid. I prefer delegation as its a standard cocoa design pattern.
Basically the problem I am having is I am unable to call a method in my Main view controller(called Recorder) from another view called Table.
The table view is loaded as a Modalview controller(presentmodalViewController) from Recorder when the user clicks on a button. The table view lets my users change from setting 1 to setting 2 and has a done button (which calls dismissmodalviewcontroller) and returns the user to the main view(Recorder).
What I want is to call a method in Recorder when the done button is clicked on Table View. This method is called Changeview and changes the setting. I am currently unable to call this method properly.
The current code I have is:
changeView method
- (void)changeView
{
[levelsView changeView];
}
TableViewController interface file
RecorderViewController*recorderViewController;
#property (nonatomic, retain) RecorderViewController *recorderViewController;
TableViewController implementation file
#synthesize recorderViewController;
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[recorderViewController changeView];
}
Method called when Done button is pressed
- (IBAction) Switch: (id) sender {
[self dismissModalViewControllerAnimated:YES];
}
The current code does not give me any errors but it also does not change the setting. I have also tried to setup notifications with no luck. Any insight into this?
Are you setting the value of recorderViewController when you initialize the tableViewController (i.e., are you setting tableViewController.recorderViewController = self in your Recorder class)? If not, then your call to [recorderViewController changeView] is sending a message to nil – which doesn't crash, but it doesn't do anything either.
As an aside, passing your TableViewController a reference to your RecorderViewController is probably not the best way for two controllers to communicate: You may want to consider using NSNotificationCenter, or passing a model object and using Key-Value Observing.
How to access a property value of AppDelegate class from someView Controller without creating reference of the delegate in view controller?
I'm not quite sure what you mean - there are multiple ways to get information from your application delegate into a view controller, and the phrase "without creating reference of the delegate" is unclear. Your options basically are:
Reference the application delegate, casting as appropriate. You would write code in your view controller class like: id propertyValue = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] myProperty];
Pass the property in when creating the view controller. This requires the view controller to have a #property declared and #synthesized for use, then you would have the app delegate just set the property on the view controller instance.
Neither of these options require that you retain a copy of your app's delegate as a #property, but the first does reference the delegate once.
[UIApplication sharedApplication].delegate
You'll also need to include the app delegate header file in your view controller and possibly typecast the delegate from id to your actual app delegate class.
#include "MyAppDelegate.h"
((MyAppDelegate *)[UIApplication sharedApplication].delegate).myProperty;
[UIApplication sharedApplication].delegate;
I'm working on a pretty simple multiview app for the iOS and I've been following a great tutorial in an Apress book. I've basically got my rootViewController instantiated and displayed with the app delegate, and I've got a number of content viewControllers (6) which I'd like to swap in and out based on user input. However, in the book they perform their switches with a button on a toolbar placed in the rootView using Interface Builder. It fires a method in rootView that loads up the new content ViewController and displays it.
My problem is that I'd like to perform the content view switch (that lies in my rootViewController instance), but I'd like to trigger the switch action with a button that's in my content view (and is therefore unavailable as my File Owner is my contentViewController, whose reference is held inside my rootViewController).
Hopefully I've explained it well enough, please let me know if I should elaborate more. I appreciate any help!
You need to pass down a reference to your root view controller (RootViewController *rootViewController) when you create your content view either in a custom init method or by just assigning it after you created it: self.contentView.rootViewController = self;.
Now inside your content view you can then call the appropriate method in the root view controller to do the switch: [self.rootViewController switchView]. This call then can be triggered inside the method that is called when you press the button (IBAction method).
So this is what you need to do:
1) Create a property inside the your content view controller of type RootViewController
#class RootViewController;
#interface MyContentViewController : NSObject {
#private
RootViewController *rootViewController;
}
#property (retain) RootViewController *rootViewController;
and make sure it retains the reference.
2) Synthesis the property and add the callback to the root view controller that switches the view:
#implementation MyContentViewController
#synthesize rootViewController;
- (IBAction) switchView:(id) sender {
[rootViewController switchToNextView];
}
-(void) dealloc {
[rootViewController release];
[super dealloc];
}
Also release your retain reference at the end.
3) Assign the root view controller to the content view inside your RootViewController:
self.contentViewController = [[[MyContentViewController alloc]
initWithNibName:#"ContentView"
bundle:nil] autorelease];
self.contentViewController.rootViewController = self;
That should be all. I hope that helps you.
Well, you could simply create an IBAction in each of your child controllers that calls:
[self.parentViewController switchToDifferentController:(int) viewNumber]
and then implement the switchToDifferentController method in your root. Other than ignore the compiler warning that parentView might not implement that method, it might work.
However, that is a bit brittle, as you'd have to assume that it was the parent calling you and that nobody will forget to implement that method.
In general, you use the "delegate" concept for a child controller to ask its parent to do something. The general idea is that you declare a group of methods as a "protocol". Think of it as a contract between objects. One object can say "I promise to implement these methods," and another can then choose to send those messages to it. The contract allows the compiler/system to check for conformance. You'll see this in UITableView, where the OS provides a standard table, but it calls back to your code to provide the individual cells as needed.
To implement a protocol, you mustdo the following: (See code segments below
Declares a protocol for the conversation
Specify that the parent will follows that protocol
Create a delegate property in your child
When the parent is about to launch the child, it assigns itself as the delegate for that child.
When the child wants to switch, it calls the parent using that protocol
#protocol myVCDelegate
- (void)switchToDifferentController:(int) viewNumber ;
#end
#interface ParentViewController : UIViewController <VCDelegate>
#property(nonatomic, assign) id <VCDelegate> delegate
childController.delegate = self;
[self.delegate switchToDifferentController:kController5];
my protocol:
#protocol ElectricalSystemEngineDelegate
-(void)didRequestMainMenu:(id)sender;
#end
I designed this protocol in order to handle dismissal of a modal View Controller inside my rootView controller. My rootView controller adopts this protocol and is declared as follows:
#import "ElectricalSystemEngineDelegate.h"
#interface RootViewController: UIViewController <ElectricalSystemEngineDelegate>
//other ivars & methods including instantiation of my modalViewController.
I use an out-of-the-box:
-(IBAction)displayElectricalViewController
-to display the modal controller... which works fine. I am, however, confused on how to proceed further with the implementation of this protocol to handle dismissal of the controller..
//The implementation of my root view controller.
-(void)didRequestMainMenu:(id)sender {
[self dismissModalViewControllerAnimated: YES];
}
Obviously, I correctly implemented the protocol by using the prescribed method. I want the method to dismiss my view controller when called. I also want it to be called by the tapping of a back button in the modalViewController.
As the apple docs say, "In some cases, an object might be willing to notify other objects of its actions so that they can take whatever collateral measures might be required." My goal is for my ElecticalViewController to notify its parent (the RootViewController) that it should be dismissed. That dismissal should be triggered by tapping the back button. How does the object accomplish this notification?
You need to add id <ElectricalSystemEngineDelegate> delegate property to your ElectricalViewController.
Then you need to assign self (RootViewController) to that delegate after you created ElectricalViewController.
Then you call [delegate didRequestMainMenu] when you dispose ElectricalViewController.
Then you need to create a method didRequestMainMenu to your RootViewController.