Reputation: 5348
Say I have 2 controllers, BarViewController and FooViewController.
FooViewController has an outlet to a UIImageView called imageView:
@property (nonatomic, weak) UIImageView *imageView;
BarViewController has an outlet to a UIButton button. BarViewController has a segue from this button to FooViewController, called BarToFooSegue (done in storyboard).
When I run the following code, and call NSLog on FooViewController.imageView.image, the result is nil, and my image won't display. Why is this the case?
// code in BarViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"BarToFooSegue"]){
NSURL *photoUrl = @"http://www.randurl.com/someImage"; // assume valid url
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:photoUrl]];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[segue.destinationViewController setImageView:imageView];
}
}
I've tried setting FooViewController.imageView to strong instead of weak, but the problem remains:
@property (nonatomic, strong) UIImageView *imageView;
Running my debugger, I notice the imageView in FooViewController is updated correctly inside prepareForSegue: but then gets re-updated a few lines afterwards to some newly allocated imageView with @property image set to nil. I am not sure what part of the control flow is causing this because it happened in lines written in assembly language.
I got my code working by adding a UIImage property to FooViewController:
@property (nonatomic, strong) UIImage *myImage;
and changing prepareForSegue: in BarViewController to pass the image instead of the imageView:
// code in BarViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"BarToFooSegue"]){
NSURL *photoUrl = @"http://www.randurl.com/someImage"; // assume valid url
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:photoUrl]];
[segue.destinationViewController setMyImage:image];
}
and modifying viewWillAppear: in FooViewController:
- (void)viewWillAppear:(BOOL)animated{
[self.imageView setImage:self.myImage];
}
Upvotes: 4
Views: 1266
Reputation: 62676
I agree with @ikuragames answer if you must set the imageView.image directly. But typically, I keep my ViewControllers' view hierarchies private.
I don't think it's ugly to add that UIImage property to Foo. I think it's actually prettier than @ikuragames correct solution.
Upvotes: 1
Reputation: 6058
Call [segue.destinationViewController view];
before you set the image, this will cause the view hierarchy to be loaded and your outlets will then be set.
Upvotes: 6
Reputation: 3740
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"BarToFooSegue"]){
NSURL *photoUrl = @"http://www.randurl.com/someImage";
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:photoUrl]];
FooViewController *vc = [segue destinationViewController];
vc.myImage = image;
}
}
Upvotes: -1
Reputation: 16946
In prepareForSegue
outlets aren't defined yet--they're nil. You're sending a message to nil, which is perfectly fine and therefore doesn't give you an error, but in this case it can lead to unexpected behavior. You can solve it by creating a temporary UIImage property, and set your image view to that image in viewDidLoad or viewWillAppear.
Upvotes: 2