Calling method to view controller delegate, won't dismiss modal view - objective-c

I have the following simple view controller class set up
#protocol ThermoFluidsSelectorViewControllerDelegate;
#interface ThermoFluidsSelectorViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) id <ThermoFluidsSelectorViewControllerDelegate> delegate;
// user hits done button
- (IBAction)done:(id)sender;
#end
#protocol ThermoFluidsSelectorViewControllerDelegate <NSObject>
-(void) didFinishSelection:(ThermoFluidsSelectorViewController *)controller fluidID: (NSString *)fluidID;
#end
the 'didFinishSeletion: fluidID:' method is defined in the master view controller and should dismiss the selector view controller when called. When the done button is pressed the following method is called:
- (IBAction)done:(id)sender
{
[[self delegate] didFinishSelection:self fluidID:nil];
}
the 'done:' method gets called (checked with an alert) but 'didFinishSelection...' is not getting called so the view will not revert back to the main screen. Any ideas?

It sounds like you have not assigned your delegate in your master view controller.
You should have something like this in your master view controller which sets up the delegate:
ThermoFluidsSelectorViewController *view = [[ThermoFluidsSelectorViewController alloc] init];
view.delegate = self;
here you can see I create the view, then set the delegate of the view back to myself.
If you are not creating the Thermo... view controller programatically, but have used a storyboard, then you can set the delegate in the prepareForSegue: method of your master view controller:
// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"MySegueID"]) {
// Get reference to the destination view controller
ThermoFluidsSelectorViewController *cont = [segue destinationViewController];
// set the delegate
cont.delegate = self;
Hope this helps.

Related

Multiple webviews in one UIViewController

Lets say I have a UIViewController with two buttons, both going (push) to another UIViewController that has two UIWebViews (showing two different PDF files), how can I make sure that only the one I choose via the button is showed?
You need to pass some information to the UIViewController which has the UIWebViews, saying which button was pressed. Then, based on that information, decide which of the UIWebViews to display.
As you are using storyboards, I suggest you look into prepareForSegue. It will allow you to set a property on the destination view controller with something like the following. You should add this to the UIViewController which contains the buttons.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"buttonOne"]) {
ExampleViewController *destViewController = segue.destinationViewController;
destViewController.buttonClicked = #"One";
} else if ([segue.identifier isEqualToString:#"buttonTwo"]) {
ExampleViewController *destViewController = segue.destinationViewController;
destViewController.buttonClicked = #"Two";
}
}
You can then use the buttonClicked property in the destination view controller to decide which you should display. If you have two separate UIWebViews, you could choose to hide one using webViewOne.hidden = YES; and show the other using webViewTwo.hidden = NO;.
However, it would probably be neater to only have a single UIWebView. You could then use prepareForSeque to pass in the URL of the PDF you would like it to display, rather than just sending the name of the button clicked.
Assuming you webView is in a view controller called SecondViewController and your buttons are in the view controller called FirstViewController
1) Create an object in your SecondViewController.h
#interface SecondViewController : UIViewController
#property (nonatomic, strong) NSString *whichButtonClicked;
#end
2) Import SecondViewController in your FirstViewController
#import "SecondViewController.h"
3) In you button IBAction method in FirstViewController.m . use this code
- (IBAction) firstButtonClicked
{
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondView"];
secondViewController. whichButtonClicked = #"first"
[self.navigationController pushViewController:secondViewController animated:YES];
}
- (IBAction) secondButtonClicked
{
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondView"];
secondViewController. whichButtonClicked = #"second"
[self.navigationController pushViewController:secondViewController animated:YES];
}
PS Don't forget. In you Storyboard. Set Storyboard ID for SecondViewController as secondView
4) In your SecondViewController.m use this code to check which button
if ([self.whichButtonClicked isEqualToString:#"first"])
{
///display first web view here
}
else
{
//display second web view here
}
Hope this helps

load images in an image view from a table in a popover

I have a view controller with an image view in it.
I have a popover with a table view in it which is anchored to a bar button in this view controller.
I would like to be able to load images into the image view by using the table in the popover.
Both the popover and the main view controller have separate view controller classes.
I have launched the popover from a segue.
How can I do this?
I am assuming that your segue takes you from your imageViewController to your popped-over tableViewController.
Then you can set your imageViewController as delegate to the tableViewController, so that you can call methods on it from the tableViewController in a decoupled manner.
MyTableViewController.h
In your tableViewController header file declare a protocol which it will expect it's delegate to follow. Place it above your #interface section:
#protocol MyTableViewControllerDelegate <NSObject>
- (void) dismissPopoverAndLoadImage:(NSString*)imageName;
#end
Also declare a property to hold a reference to it's delegate:
#property (nonatomic, weak) id <MyTableViewControllerDelegate> delegate;
The protocol declares the method signature that your tableView will expect to be able to call on its delegate. It allows it to send back some data, and get itself dismissed. The delegate (in this case, your imageViewController) will have to implement this method.
MyTableViewController.m
The method is called on the delegate when a table cell is selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
NSString* imageName = cell.textLabel.text;
[self.delegate dismissPopoverAndLoadImage:imageName];
}
MyImageViewController.h
include MyTableViewController.h and add the delegate protocol to the #interface.
#include "TableViewController.h
#interface MyImageViewController: UIViewController <MyTableViewControllerDelegate>
Declare a property to hold a reference to your UIPopOverController so that you can send it a dismiss message:
#property (nonatomic, weak) UIPopoverController* seguePopoverController;
(these steps could be moved to your .m file's category extension for better encapsulation).
MyImageViewController.m
You will set the delegate property in MyImageViewController's prepareForSegue method, which gets called when the segue is invoked.You will also set the reference to the popoverController here.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"popoverTable"]) {
self.seguePopoverController = [(UIStoryboardPopoverSegue*)segue popoverController];
[segue.destinationViewController setDelegate:self];
}
}
}
Lastly, you implement the tableViewController's delegate method:
- (void) dismissPopoverAndLoadImage:(NSString*)imageName
{
self.imageView.image = [UIImage imageNamed:imageName];
[self.seguePopoverController dismissPopoverAnimated:YES];
}
update
Aside from the fact that the popOverController itself is a slightly unusual entity (a controller without a view, inheriting directly from NSObject), most of this is the standard delegation pattern. You could simplify it somewhat by using a bit of indirection and runtime checking in didSelectRowAtIndexPath:
if ([[self delegate] respondsToSelector:#selector(dismissPopoverAndLoadImage:)])
[[self delegate] performSelector:#selector(dismissPopoverAndLoadImage:)
withObject:imageName];
In this case you would not need to define the protocol or <adhere> to it, and you wouldn't need to #import MyTableViewController. However the compiler would give you no help if you did not implement the method correctly. Which, as you can see from my earlier mistake, is probably unwise.

How to modify the sender ViewController from the called ViewController?

I have a login view controller called from a Storyboard segue. When the user logs in correctly, I need to close the current ViewController, return to the caller ViewController and hide the login button.
How can I refer to the sender ViewController, to hide the button? Do I have to pass an instance of the root ViewController to the login or is there another way?
Use delegation.
For your DetaliViewControler create a protocol, like
#protocol DetailViewControllerDelegate <NSObject>
-(void)successFullyLoggedInOnController:(DetailViewController *) controller;
#end
add a delegate declaration to DetailViewController's interface like
#property (weak) id<DetailViewControllerDelegate> delegate;
Make the MasterViewController conform to the protocol.
-(void)successFullyLoggedInOnController:(DetailViewController *) controller
{
[self.loginButton setHidden:YES];
}
Now just before the MasterViewController displays the DetailViewController, doe something like
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"DetailViewSegue"]) {
DetailViewController *vc = segue.destinationViewController;
vc.delegate = self;
}
}
In the DetailViewController once the credential where entered and verified call
[self.delegate successFullyLoggedInOnController:self];
and dismiss the DetailViewController
You should use delegation. Make the VC with the button the other one's delegate. When log in is successful, you dismiss your VC and call the delegate method to hide the button.

Error in Pushing a TableViewCell to Another ViewController

I trying to pass the data from TableViewCell to the another ViewController.But No data Displaying in the another ViewController.here is my Code
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
PeripheralManager *objSelected=[device objectAtIndex:indexPath.row];
[self prepareForSegue:#"TableDetails" sender:objSelectedDevice];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"TableDetails"])
{
DetailViewController *detail=segue.destinationViewController;
detail.dataArray=device;
}
}
Error Message
nested push animation can result in corrupted navigation bar
2012-10-24 12:01:39.805 [3182:707] nested push animation can result in corrupted navigation bar
2012-10-24 12:01:40.164 [3182:707] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2012-10-24 12:01:40.167 [3182:707] Finishing up a get navigation transition in an unexpected state. Navigation Bar subview tree might corrupted.
You do not need this:
[self.navigationController pushViewController:mdc animated:YES];
That is what Segue will do automatically
Also, you are having 3 lines that will load view controller - see below for comments:
NSInteger row=[indexPath row];
NSString *value=[device objectAtIndex:row];
MeBleDetailViewController *mdc=[self.storyboard instantiateViewControllerWithIdentifier:#"MeBleDetailViewController"];
mdc.deviceName=value;
[self presentModalViewController:mdc animated:YES]; // Load ViewController
[UIView commitAnimations];
[self performSegueWithIdentifier:#"TableDetails" sender:[device objectAtIndex:indexPath.row]]; // Load ViewController
[self.navigationController pushViewController:mdc animated:YES]; // Load ViewController
That is why you are getting that error: nested push animation can result in corrupted navigation bar
Also, If you have configured the segue from table cell to another view controller then you don't need anything in didSelectRowAtIndexPath method.
Edit:
Whatever data you want the pushed view controller to have - put it in prepareforSegue method instead of didSelectRowAtIndexPath
If you create a segue from table cell to view controller then you don't need to execute the following as this method is to execute the segue programmatically.
[self performSegueWithIdentifier:#"TableDetails" sender:[device objectAtIndex:indexPath.row]];
Remove your extra code Only do this-
In DetailViewController.h
#property(nonatomic, retain)NSMutableArray *dataArray;
In DetailViewController.m
#synthesize dataArray = _dataArray;
Now In TableViewController.m Just write this -
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"TableDetails"])
{
DetailViewController *detailViewObject = segue.destinationViewController;
detailViewObject.dataArray = anyArray;
}
}
Here I'm passing NSMutableArray.
OK. Let's say you have two viewcontrollers FirstViewController and SecondViewController.
In FirstViewController you have a tableview and of course tableviewcell. In SecondViewControlleryou need to display data.
So in SecondViewController.h you need to set a propery of some variable, in this case it is of id type #property (strong, nonatomic) id secDetailItem;. Synthesize it in SecondViewController.m and add a setter method like this
-(void)setDetdetailItem:(id)newSecdetailItem{
if (secDetailItem != newSecdetailItem) {
secDetailItem = newSecdetailItem;
// Update the view.
[self configureView];//This method is needed to update view if there are some changes in that view.
}
}
So then in FirstViewController.h import SecondViewController.h and add property #property (strong, nonatomic) SecondViewController *secondViewController; then
synthesize. In FirstViewController.m file in this delegate method do following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
self.secondViewController.secDetailItem=//set your data should be passed.
//Also if you need to push viewcontroller add pushviewcontroller:SecondViewController, or use IB to connect tableview cell and SecondViewController together with push method.
}
In this case you will not need to use perform segue. The Setter method will work as soon as you set to the secDetailItem something.
Also if you need to update your view in SecondViewController add this method to it.
- (void)configureView
{
if (self.secDetailItem) {
self.textLabel.text=self.secDetailItem;//Data passed from FirstViewController
}
}
This is all you need to do. Sorry if it is complicated. Ask any question.
It might have something to do with this line:
[UIView commitAnimations];
You can delete it if you don't need it.

Protocol is not calling methods

I have a modal view which gets the user to select some data to add to a table. When the user presses a save button, the modal view should disappear and send the required data back to the view controller that presented the modal view for further processing. To achieve this, I have set up a protocol. The protocol method in the original view controller does not get called. My code is below, what am I doing wrong?
The header file (modal view controller):
#protocol AddTAFDataSource;
#interface AddTAFViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
id<AddTAFDataSource> dataSource;
NSString *newICAOCode;
}
#property (nonatomic, assign) id<AddTAFDataSource> dataSource;
- (IBAction)saveButtonPressed;
#end
#protocol AddTAFDataSource <NSObject>
- (void)addNewTAF:(AddTAFViewController *)addTAFViewController icao:(NSString *)icaoCode;
#end
The implementation file (modal view controller):
#import "AddTAFViewController.h"
#import "TAFandMETARViewController.h"
#implementation AddTAFViewController
#synthesize dataSource;
...
- (IBAction)saveButtonPressed {
[self.dataSource addNewTAF: self icao: newICAOCode];
}
#end
Presenting view controller header file:
#import "AddTAFViewController.h"
#interface TAFandMETARViewController : UITableViewController <AddTAFDataSource> {
}
#end
And finally, the presenting view controller:
#import "AddTAFViewController.h"
...
- (void)insertNewObject:(id)sender {
AddTAFViewController *addTAFViewController = [[AddTAFViewController alloc] initWithNibName: #"AddTAF" bundle: [NSBundle mainBundle]];
addTAFViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[(AddTAFViewController *)self.view setDataSource: self];
[self presentModalViewController: addTAFViewController animated: YES];
addTAFViewController = nil;
[addTAFViewController release];
}
- (void)addNewTAF:(AddTAFViewController *)addTAFViewController icao:(NSString *)icaoCode {
newICAO = icaoCode;
[self dismissModalViewControllerAnimated: YES];
}
Just to remind, it is the above -(void)addNewTAF: method that does not get messaged. Any help/pointers in the right direction are much appreciated.
Replace:
[(AddTAFViewController *)self.view setDataSource: self];
With:
[addTAFViewController setDataSource:self]
After all, the dataSource is a property of the controller, not a controller's view.
Rather than trying to use a separate object (your dataSource) to pass data between the two view controllers, you could simply use add properties to contain the data directly in the view controller you're going to present modally (here, the AddTAFViewController).
Then in the method you use to dismiss the modal view controller, before dismissing it you can send [self modalViewController] to get the modal view controller, and at that point the parent view controller can send it any messages it wants. That would allow you to grab whatever data you need from the modal view controller, so you wouldn't need the data source and the protocol at all.
You are wrong at this point:
[(AddTAFViewController *)self.view setDataSource: self];
you should write this instead:
addTAFViewController.dataSource = self;