Im trying to set a UIImageView from a segue, and for some reason the image is not getting set..
Heres the .h files of my class that subclassed a UIViewController
#interface PhotoDisplayViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIImageView *photoView;
-(void)setPhoto:(UIImage *)photo;
#end
and heres the setPhoto
-(void)setPhoto:(UIImage *)photo{
NSLog(#"PHOTO %#", photo);
_photoView.image = photo;
NSLog(#"MYPHOTO %#", _photoView.image);
}
when i call setPhoto from prepare for segue, i see this in the console
2012-12-16 13:26:22.129 TestApp[2183:907] PHOTO <UIImage: 0x1fd7cd80>
2012-12-16 13:26:22.130 TestApp[2183:907] MYPHOTO (null)
Why is this happening?
It looks like _photoView is probably nil. It may not be set when loading the nib. Make sure you've wired it up properly in IB. Or perhaps you are calling -setPhoto: before the view has loaded.
I would advise you to change the weak to strong and take a look on this link.
Objective-C - weak property - getter autoreleases (Automatic Reference Counting)
I think you called setPhoto: method as following. In this situation, first setPhoto: is called and after that viewDidLoad method is called in PhotoDisplayViewController. That's why photoView is nil. Actually viewDidLoad method should be called in PhotoDisplayViewController.So you should change the code slightly.
-(void)someActionMethod{
[self performSegueWithIdentifier:yourIdentifier sender:nil];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:yourIdentifier]) {
photoDisplayVC = [segue destinationViewController];//photoDisplayVC is a object of PhotoDisplayViewController
[photoDisplayVC setPhoto:imageObject];
}
The change Code: In this situation first viewDidLoad method is called and after that setPhoto: method is called in PhotoDisplayViewController. Then you will get image.
-(void)someActionMethod{
[self performSegueWithIdentifier:yourIdentifier sender:nil];
[photoDisplayVC setPhoto:imageObject];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:yourIdentifier]) {
photoDisplayVC = [segue destinationViewController];//photoDisplayVC is a object of PhotoDisplayViewController
}
I think it will be helpful to you.
Related
I have an UITableViewController created with storyboard.
Than I have an anchor that call a popOver created with storyboard as well. The popover is another UItableViewController, when I click on a row I should call back the first controller and pass an object.
I have tried this in the popover object:
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"Fortune"]){
NSIndexPath *indexPath = (NSIndexPath *)sender;
ASFortuneTeller * aController = [segue destinationViewController];
[aController setWYPT:[allWYPTs objectAtIndex:indexPath.row]];
}
}
Basically I need to pass a NSMutableDictionary bag to the first UITableViewController.
But I have noticed that in this way I create a new ASFortuneTeller object , that is not what I want... I want just to call back the first controller and pass an object.
How can I do it?
A quick solution (when it involves always the same two classes) could be:
In the .h file of the first view controller, define a method (or just a property):
-(void)selectedWYPT:(NSMutableDictionary*)wypt;
Within the .h file of your second view controller make a property
#property FirstUIViewController *firstView;
In the first view controller, you will open the second view controller via a segue, so there you can use:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueIdentifier"]) {
SecondUIViewController *destination = segue.destinationViewController;
destination.firstView = self;
}
}
When the row is selected in the second view you can use
if (self.firstView)
[self.firstView setWYPT:[allWYPTs objectAtIndex:indexPath.row]];
to pass the data back to the first view.
As said, this would be a quick solution when always the same two classes are involved.
An other way would be to use protocols. When the first view controller won't always be FirstUIVierController, you may use something like this:
SecondUIViewController.h
#class SecondUIViewController;
#protocol SecondUIViewControllerDelegate <NSObject>
-(void)secondUIViewController:(SecondUIViewController*)controller didSelectWYPT:(NSMutableDictionary*)wypt;
#end
#interface SecondUIViewController : UIViewController
#property id<SecondUIViewControllerDelegate> delegate;
#end
SecondUIViewController.m
where the row is selected:
if (self.delegate && [self.delegate respondsToSelector:#selector(secondUIViewController:didSelectWYPT:)])
[self.delegate secondUIViewController:self didSelectWYPT:[allWYPTs objectAtIndex:indexPath.row]];
AnyOtherUIViewController.h
#import "SecondUIViewController.h"
#interface AnyOtherUIViewController : UIViewController <SecondUIViewControllerDelegate>
...
...
AnyOtherViewController.m
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueIdentifier"]) {
SecondUIViewController *destination = segue.destinationViewController;
destination.delegate = self;
}
}
-(void)secondUIViewController:(SecondUIViewController*)controller didSelectWYPT:(NSMutableDictionary*)wypt {
//do something with the data
}
Im trying to pass data from a textfield in ViewController2 to a label in ViewController. It do not seem to work and i get no errors. What am i missing in my Segue?
ViewController2.h
#interface ViewController2 : UIViewController {
IBOutlet UITextField *HomeTeam;
}
ViewController2.m
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[segue.identifier isEqualToString:#"HomeTeam"];
NSString *homeTeamText = HomeTeam.text;
ViewController *vc = [segue destinationViewController];
vc.HomeTeamString = homeTeamText;
}
ViewController.h
#property (weak, nonatomic) IBOutlet UILabel *HomeTeamLabel;
#property (weak, nonatomic) NSString *HomeTeamString;
ViewController.m
#synthesize HomeTeamString, HomeTeamLabel;
You're missing an if. Unless you've copied and pasted wrong...
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"HomeTeam"]) {
NSString *homeTeamText = HomeTeam.text;
ViewController *vc = [segue destinationViewController];
vc.HomeTeamString = homeTeamText;
}
}
[segue.identifier isEqualToString:#"HomeTeam"] returns a BOOL that lets you know whether or not this is the segue that's being performed.
Even if this isn't actually causing a problem (in this example), you should be doing this anyway. I will update this answer as more information is given.
Also, be aware that in this particular code, you're setting an NSString property on the destination view controller. Not a label...
Assuming everything on the storyboard is correct, be sure to add the following to your viewDidLoad or viewWillAppear method:
HomeTeamLabel.text = HomeTeamString;
And for good measure, you may need to change HomeTeamString from weak to strong.
EDIT: After some researching... it seems that the HomeTeamString property in your destination view controller MUST be strong as opposed to weak. Leaving it as weak allows it to be released as soon as the prepareForSegue method ends (it doesn't have an owner between prepareForSegue ending and viewDidLoad in the destination view starting). Changing it to strong will fix the problem ensuring it will only be released if the destination view controller is released.
Please verify that, you set the identifier of the segue in storyboard.
In your case the identifier will be HomeTeam.
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.
I am trying to pass data from a UITextView in one view controller to UITextView in another view controller. My application is using Storyboards.
in the first view controller I say:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
LyricKaraokeViewController *karaokeController = segue.destinationViewController;
karaokeController.hidesBottomBarWhenPushed = YES;
SongDoc *song = [[SongDoc alloc] initWithTitle:#"Title" lyrics:#"Test" thumbImage:nil];
karaokeController.detailItem = song;
NSLog(#"%#", karaokeController.detailItem.data.lyrics);
}
The NSLog outputs the appropriate text
In my second view controller I declare this interface:
#class SongDoc;
#interface LyricKaraokeViewController : UIViewController
#property (nonatomic, retain) SongDoc * detailItem;
#end
and this implementation ( just showing viewDidLoad for simplicity ):
- (void)viewDidLoad
{
NSLog(#"%#", self.detailItem.data.lyrics);
[super viewDidLoad];
}
and I confirm that there is the properties data.lyrics in the detailItem because we are passing in a SongDoc object and I output the SongDocs contents in prepareForSegue just prior....
My problem is in the second view controller I am receiving an error saying the detail item doesn't declare the property lyrics
But I know this is untrue so why is this happening?
any help would be great thanks :)
#class SongDoc;
only tells the compiler that SongDoc is a class, but does not read the implementation file. You have to
#import "SongDoc.h"
instead, so that the compiler knows which properties are declared by the class.
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.