Error in Pushing a TableViewCell to Another ViewController - objective-c

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.

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.

Setting a property in PrepareForSegue is null for ViewDidLoad but correct later on

I have searched up and down on this and found a few that were similar but not my exact problem. In my detail view controller I have a segue that opens a popover table view controller. In the PrepareForSegue method, I set a property of the table view, like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"StateSegue"])
{
popover = [(UIStoryboardPopoverSegue *)segue popoverController];
StatePickerController *statePicker = (StatePickerController *)popover.contentViewController;
statePicker.delegate = self;
statePicker.pickerType = #"states";
}
}
I pass both the delegate and the pickerType value. In my table view, StatePickerController, in the header I declared these properties like this:
#property (nonatomic,assign) id<StatePickerDelegate> delegate;
#property (nonatomic,strong) NSString *pickerType;
I know these values are getting passed into my popover controller correctly because later on in didSelectRowAtIndexPath I can reference them both and I get the right delegate and "state", however in the viewDidLoad method, both are still (null).
- (void)viewDidLoad
{
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0);
self.states = [NSMutableArray array];
NSLog(#"%#",self.delegate); //returns (null)
NSLog(#"%#",self.pickerType); //returns (null)
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%#",self.pickerType); //corectly logs "state"
NSString *state = [_states objectAtIndex:indexPath.row];
[self.delegate stateSelected:state]; //correctly runs method on detailviewcontroller
}
Is there some point after the viewDidLoad method where these properties get set? I need access to these values right when the view loads because I need to determine what data populates in the table based on what is passed from the detailviewcontroller.
viewWillAppear is called after viewDidLoad. Use that instead to check the values after the prepareForSegue method.

Is there anyway to know where a popover was presented from?

I'm writing an iPad app and one of my screens has lots of small buttons that when pressed will display one sentence of text in a popover originating from that button. Currently all popovers are created using the storyboard and I store the popover controller in my UIViewController as such:
#property (nonatomic, strong) UIPopoverController *myPopoverController;
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue isKindOfClass:[UIStoryboardPopoverSegue class]])
{
UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *)segue;
self.myPopoverController = popoverSegue.popoverController;
}
}
However, I can't figure out a good way to deal with rotation. Right my didRotate method looks like so:
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if (self.myPopoverController)
{
[self.myPopoverController dismissPopoverAnimated: NO];
[self.myPopoverController presentPopoverFromRect:?????? inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:NO];
}
}
However, I don't know where to present the popovers from given that they could have originated from any of the small buttons on my screen. Any suggestions? Remember that these are VERY simple popovers, thus a whole bunch of new code is not ideal.
Your best bet may be to make another property in your main view controller that keeps a reference to the button pressed. Something like:
#property (nonatomic, strong) UIPopoverController *myPopoverController;
#property (nonatomic, weak) UIView *popoverButton;
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue isKindOfClass:[UIStoryboardPopoverSegue class]])
{
UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *)segue;
self.myPopoverController = popoverSegue.popoverController;
//The sender in prepareForSegue should be the view used to initiate the segue.
popoverButton = (UIView *)sender;
}
}
That done, you can modify your rotation code thusly:
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if (self.myPopoverController)
{
[self.myPopoverController dismissPopoverAnimated: NO];
[self.myPopoverController presentPopoverFromRect:popoverButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:NO];
}
}
Keeping a reference to the pressed button takes up no more resources that storing a pointer, and keeping the reference weak should avoid retain cycles (after all, your view controller does not own the button, the button's superview owns it).

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

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.