I have three ways of writing this code. The third way confuses me.
First way works fine.
//.h
#property (weak, nonatomic) IBOutlet UIImageView *picImageStage;
//.m
NSString *name = [NSString stringWithFormat:#"allen.png"];
UIImage *image = [UIImage imageNamed:name];
UIImageView *t = [[UIImageView alloc]initWithImage:image];
self.picImageStage = t;
Second way works fine.
//.h
#property (retain, nonatomic) IBOutlet UIImageView *picImageStage;
//.m
NSString *name = [NSString stringWithFormat:#"allen.png"];
UIImage *image = [UIImage imageNamed:name];
self.picImageStage = [[UIImageView alloc]initWithImage:image];
Third way turns wrong.
//.h
#property (weak, nonatomic) IBOutlet UIImageView *picImageStage;
//.m
NSString *name = [NSString stringWithFormat:#"allen.png"];
UIImage *image = [UIImage imageNamed:name];
self.picImageStage = [[UIImageView alloc]initWithImage:image];
I don’t understand the reason. Could anyone help me? Thanks :D
In the 3rd snippet you have declared the #property as weak, and the UIImageView will be deallocated immediately. Because a weak relationship will be nil'd when there is no strong relationship to the same object.
In the 1st snippet, which is almost the same, you assigned the UIImageView to a local variable first. This local variable uses a strong relationship implicitly. If you leave the scope of the local strong variable (i.e. the method where you run this code) the property will be deallocated too, except if you create another strong assignment before leaving the scope of the variable. Which will for example happen if you add the UIImageView as a subView of another view. Adding a view to another creates a strong relationship.
Let's examine all three, because that might be the easiest way to explain this to you.
Number 1
//first method
//.h
#property (weak, nonatomic) IBOutlet UIImageView *picImageStage;
//.m
NSString *name = [NSString stringWithFormat:#"allen.png"];
UIImage *image = [UIImage imageNamed:name];
UIImageView *t = [[UIImageView alloc]initWithImage:image];
self.picImageStage = t;
You're initializing a local variable UIImageView with a retain count of +1. Because weak properties don't call implicit retains on their values, you don't own the value stored in self.picImageStage, which means you're one lucky camper because as soon as the function that declares that local UIImageView passes out of scope, your variable is deallocated.
Number 2
//second method
//.h
#property (retain, nonatomic) IBOutlet UIImageView *picImageStage;
//.m
NSString *name = [NSString stringWithFormat:#"allen.png"];
UIImage *image = [UIImage imageNamed:name];
self.picImageStage = [[UIImageView alloc]initWithImage:image];
This one gets into those implicit retains I was talking about. The compiler expands this line:
self.picImageStage = [[UIImageView alloc]initWithImage:image];
out to
self.picImageStage = [[[UIImageView alloc]initWithImage:image]retain];
Meaning that you own it, and are free to do with it what you please.
Number 3
//third method
//.h
#property (weak, nonatomic) IBOutlet UIImageView *picImageStage;
//.m
NSString *name = [NSString stringWithFormat:#"allen.png"];
UIImage *image = [UIImage imageNamed:name];
self.picImageStage = [[UIImageView alloc]initWithImage:image];
Not only are you assigning to a weak property from a variable that you don't own, as soon as the assignment is called, your variable won't store the value! It's because, again, weak pointers do not call implicit retains on their assignments, meaning you have no control over how long the variable sticks around, and I'm willing to bet that it doesn't stay alive for very long!
Here you use weak property instead of retain and also alloc it directly with UIImage...
Also in first stage when you assign UIImageView to the picImageStage then its work fine because its directly equal and store in the picImageStage.. thats the difference..
if you use
#property (retain, nonatomic) IBOutlet UIImageView *picImageStage;
instead of
#property (weak, nonatomic) IBOutlet UIImageView *picImageStage;
then its work fine with this reason..
Just try this
//.h
#property (weak, nonatomic) IBOutlet UIImageView *picImageStage;
//.m
NSString *userName = #"allen";
NSString *imgName = [NSString stringWithFormat:#"%#.png",userName];
UIImage *image = [UIImage imageNamed:imgName];
[self.picImageStage setImage:image];
Related
I'm trying to displaying an image from file on my ViewController in Xcode. I have in my ViewController.h:
UIImageView *image;
#property(nonatomic,retain) IBOutlet UIImage* image ;
ViewController.m
- (id)initWithImage:(UIImage *)image
{
UIImageView * imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"nameoffile.jpg"]];
self.image = image;
[image release];
}
But no luck. It won't let me link my UIImage instance in the xib.
Try to use this one.You know you can't get IBOutlet on UIImage. So you need to write UIImageView instead of UIImage.
#property(nonatomic,retain) IBOutlet UIImageView* image ;
As the UIImageView is created when the NIB file is loaded, there is no need to make it an instance variable, so remove it from your header file:
UIImageView *image;
Next ensure that your UIImageView outlet is actually connected correctly to the image view within the NIB file. You will see a filled circle when it is connected (this is from a Beginning iOS Development example, which uses ARC, so ignore the weak keyword):
And finally you should be able to set the image view's image in the viewDidLoad method (there is no need to use an instance variable for the UIImage either, as it will be retained within the UIImageView):
- (void)viewDidLoad
{
[super viewDidLoad];
imageView.image = [UIImage imageNamed:#"nameoffile.jpg"];
}
Check the Error of your code.
#property(nonatomic,retain) UIImageView *imageView;
#property(nonatomic,retain)UIImage* image ;
- (id)initWithImage:(UIImage *)image
{
_imageView = [[UIImageView alloc] initWithImage:image];
_imageView.frame=self.view.frame;
[self.view addSubView:_imageView];
[image release];
}
I am not getting my image working. Can anyone tell me what I am doing wrong here?
musclePicture is a string in format of picture.png
1st view:
NSString *muscleURL = [[self.muscleArray objectAtIndex:indexPath.row]objectForKey:#"musclePicture"];
specificExerciseTableViewController.muscleImage = [UIImage imageNamed:muscleURL];
2nd view:
detailViewController.muscleImage = self.muscleImage;
3rd view:
self.image = [[UIImageView alloc]initWithImage:muscleImage];
[image release];
Edit:
MusclesTableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *muscleURL = [[self.muscleArray objectAtIndex:indexPath.row]objectForKey:#"musclePicture"];
specificExerciseTableViewController.muscleImage = [UIImage imageNamed:muscleURL];
}
SpecificExerciseTableViewController.h
#property (nonatomic, retain) UIImage *muscleImage;
SpecificExerciseTableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
detailViewController.muscleImage = self.muscleImage;
}
DetailViewController.h
#property (nonatomic, retain) UIImage *muscleImage;
#property (nonatomic, retain) IBOutlet UIImageView *image;
DetailViewController.m
- (void)viewDidLoad
{
self.image.image = muscleImage;
}
Try by giving a frame to your UIImageView after:
self.image = [[UIImageView alloc]initWithImage:muscleImage];
[image setFrame:CGRectMake(0,0,50,50)];
[image release];
Try this code snippet:
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[animal imageURL]]];
UIImage *animalImage = [[UIImage alloc] initWithData:imageData];
Assign it your UIImageView or UIImage.
I think you forgot to add image as subView to your view.
like this
[self.view addSubView:image];
or you are having a class variable that is connected to .xib file as an IBOutlet and you are reinitializing it using alloc and init, if that is the case then you have to do
self.image.image = [UIImage imageNamed: muscleImage];
I'm not sure why, but I'm having trouble passing a data object (NSManagedObject) to a view controller when that view controller is being instantiated inside another one. When I log the object before it is passed, it has all of its data, but after it gets to the view controller, it is empty. It seems like this should be fairly straight-forward, but for some reason it's just not getting there.
The code which assigns the NSManagedObject to the accepting view controller in the implementation file is: viewController.thisCard = aCard;
Any help is much appreciated. Thanks in advance.
This is the header for the view controller which scrolls:
#interface HandViewController : UIViewController
<UIScrollViewDelegate>
{
IBOutlet UIScrollView *scrollView;
IBOutlet UIPageControl *pageControl;
BOOL pageControlIsChangingPage;
NSManagedObjectContext *context;
}
#property (nonatomic, retain) UIView *scrollView;
#property (nonatomic, retain) UIPageControl *pageControl;
#property (nonatomic, retain) NSManagedObjectContext *context;
/* for pageControl */
- (IBAction)changePage:(id)sender;
#end
this is the viewDidLoad in the implementation of that object
- (void)viewDidLoad
{
scrollView.delegate = self;
[self.scrollView setBackgroundColor:[UIColor blackColor]];
[scrollView setCanCancelContentTouches:NO];
scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView.clipsToBounds = YES;
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Game" inManagedObjectContext:[self context]];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedGames = [self.context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
Game *aGame = [fetchedGames objectAtIndex:0];
CGFloat cx = 0;
for (Card *aCard in aGame.turnUpcoming.hand) {
CardViewController *viewController = [[CardViewController alloc] initWithNibName:#"CardViewController" bundle:nil];
CGRect rect = self.view.frame;
rect.size.height = scrollView.frame.size.height - 50;
rect.size.width = scrollView.frame.size.width - 50;
rect.origin.x = ((scrollView.frame.size.width - rect.size.width) / 2) + cx;
rect.origin.y = ((scrollView.frame.size.height - rect.size.height) / 2);
viewController.view.frame = rect;
viewController.thisCard = aCard;
[scrollView addSubview:viewController.view];
cx += scrollView.frame.size.width;
[viewController release];
}
self.pageControl.numberOfPages = [aGame.turnUpcoming.hand count];
[scrollView setContentSize:CGSizeMake(cx, [scrollView bounds].size.height)];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
UPDATE: Per request, I am adding a bit of code from the view controller
This is the header for CardViewController
#interface CardViewController : UIViewController {
UILabel IBOutlet *cardValue;
UILabel IBOutlet *cardInstruction;
UILabel IBOutlet *cardHint;
UILabel IBOutlet *cardTimer;
UIButton IBOutlet *playCardButton;
Card *thisCard;
}
#property (nonatomic, retain) UILabel IBOutlet *cardValue;
#property (nonatomic, retain) UILabel IBOutlet *cardInstruction;
#property (nonatomic, retain) UILabel IBOutlet *cardHint;
#property (nonatomic, retain) UILabel IBOutlet *cardTimer;
#property (nonatomic, retain) UIButton IBOutlet *playCardButton;
#property (nonatomic, retain) Card *thisCard;
#end
Here is the viewDidLoad from the implementation of CardViewController
- (void)viewDidLoad
{
[super viewDidLoad];
if ([thisCard isObjectValid]) {
cardValue.text = [thisCard.value stringValue];
}
}
UPDATE: new code for card loop in viewDidLoad and new CardView code, per suggestions below
loop in viewDidLoad:
for (Card *aCard in aGame.turnUpcoming.hand) {
CGRect rect = scrollView.frame;
rect.size.height = scrollView.frame.size.height - 50;
rect.size.width = scrollView.frame.size.width - 50;
rect.origin.x = ((scrollView.frame.size.width - rect.size.width) / 2) + cx;
rect.origin.y = ((scrollView.frame.size.height - rect.size.height) / 2);
tempCardView = [[CardView alloc] initWithFrame:rect];
tempCardView.thisCard = aCard;
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"CardView" owner:self options:nil];
tempCardView = [nibObjects objectAtIndex:0];
[scrollView addSubview:tempCardView];
cx += scrollView.frame.size.width;
[tempCardView release];
}
header file CardView.h
#interface CardView : UIView {
UILabel IBOutlet *cardValue;
UILabel IBOutlet *cardInstruction;
UILabel IBOutlet *cardHint;
UILabel IBOutlet *cardTimer;
UIButton IBOutlet *playCardButton;
Card *thisCard;
}
#property (nonatomic, retain) UILabel IBOutlet *cardValue;
#property (nonatomic, retain) UILabel IBOutlet *cardInstruction;
#property (nonatomic, retain) UILabel IBOutlet *cardHint;
#property (nonatomic, retain) UILabel IBOutlet *cardTimer;
#property (nonatomic, retain) UIButton IBOutlet *playCardButton;
#property (nonatomic, retain) Card *thisCard;
#end
implementation file CardView.m
#implementation CardView
#synthesize cardHint;
#synthesize cardTimer;
#synthesize cardValue;
#synthesize cardInstruction;
#synthesize playCardButton;
#synthesize thisCard;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
if ([thisCard isObjectValid]) {
cardValue.text = [thisCard.value stringValue];
cardInstruction.text = thisCard.instruction;
cardHint.text = thisCard.hint;
cardTimer.text = [thisCard.time stringValue];
}
}
return self;
}
#end
UPDATED: 6/16 - Added XIB screen shot
CardView.xib
Seems like your CardViewController should actually be a UIView. As Apple says: " A single view controller typically manages the views associated with a single screen’s worth of content, although in iPad applications this may not always be the case."
(After changes to UIView) in your loop, you're creating one tempCard and then loading another. If you've linked tempCardView in IB (see below), then all you need is:
for (Card *aCard in aGame.turnUpcoming.hand) {
CGRect rect;
rect.size.width = scrollView.frame.size.width - 50;
rect.size.height = scrollView.frame.size.height - 50;
rect.origin.x = ((scrollView.frame.size.width - rect.size.width) / 2) + cx;
rect.origin.y = ((scrollView.frame.size.height - rect.size.height) / 2);
[[NSBundle mainBundle] loadNibNamed:#"CardView" owner:self options:nil]; //magically connected to tempCardView.
self.tempCardView.frame = rect;
self.tempCardView.thisCard = aCard;
[scrollView addSubview:tempCardView];
cx += scrollView.frame.size.width;
self.tempCardView = nil;
}
So, you have two XIBs, your main one that loads with your viewController, and one (CardView) that gets loaded once for each card.
Now, inside CardView, you're trying to set up the labels too soon. During initWithFrame (which in the above case is called by loadFromNib), you don't have access yet to thisCard. You'll need a setter for thisCard, which will be called any time a value is assigned to thisCard property:
-(void) setThisCard: (Card *) newCard {
if (thisCard == newCard) return;
[newCard retain];
[thisCard release];
thisCard = newCard;
self.cardValue.text = [thisCard.value stringValue];
self.cardInstruction.text = thisCard.instruction;
self.cardHint.text = thisCard.hint;
self.cardTimer.text = [thisCard.time stringValue];
}
Now in Interface Builder,
Create CardView.xib with a single UIView with, but set FileOwner to your VC, NOT to CardView because that's who's going to load it (and who has the tempCardView property).
Set the view's type to CardView, then hook it to thetempCardView property of FileOwner (aka HandViewController)
Put all your other card parts inside that CardView and hook them up to that CardView's properties.
You should be all set.
I have the following code:
#interface NeighborProfileViewController : UIViewController {
UIImageView * profPic;
UITextView * text;
UIButton * buzz;
NSString * uid;
NSMutableData *responseData;
NSDictionary * results;
}
#property (nonatomic, retain) IBOutlet UIImageView * profPic;
#property (nonatomic, retain) IBOutlet UITextView * text;
#property (nonatomic, retain) IBOutlet UIButton * buzz;
#property (nonatomic, retain) NSString * uid;
#end
Here's what I have in the viewDidLoad on the .m file
#implementation NeighborProfileViewController
#synthesize uid;
#synthesize profPic;
#synthesize buzz;
#synthesize text;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = //some URL to the picture;
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[[UIImage alloc] initWithData:data] autorelease];
self.profPic = [[UIImageView alloc] initWithImage: img];
}
I think I wired up the UIImageView via IB correctly. I have an outlet from the UIImageView to the profPic in the Files Owner. What am I doing wrong?
If you are setting a default image is that staying visible after you call self.profPic = [UIImageView] or is it being removed from the screen?
I think this problem comes when self.profPic releases the old image view to replace it with the one you've just created. The old UIImageView instance, along with all the properties which you defined in IB, are probably automatically removed from the superview. Which is why you're not seeing the downloaded image.
If you used IB to create the UIImageView then you don't want to create a new ImageView and assign it to profPic (which is already a fully instantiated UIImageView). Try calling [profPic setImage:img]; which will just change the image in the profPic imageview.
#Equinox use
[profPic setImage:img];
instead of
self.profPic = [[UIImageView alloc] initWithImage: img];
although using autorelease is not an issue here.
you can also do something like this
UIImage *img = [[UIImage alloc] initWithData:data] ;
[profPic setImage:img];
[img release];
the problem with
self.profPic = [[UIImageView alloc] initWithImage: img];
is that you are now creating another memory location for profPic and that means profPic will no longer point to the UIImageView in your IB any more
Without getting into OpenGL (Quartz 2D is OK):
Let's say I have an image which I want to move across a map in some fluid way. For instance, an image of a plane "flying" across the map. I've been able to do this using an MKAnnotation, an NSTimer and fiddling with the rate of lat/lon change and timer rate. However, I assume this isn't ideal, although the results look pretty decent. Can you think of a better way?
Now let's say that I want this image to be animated (think: animated gif). I can't do the usual UIImageView with a series of animationFrames because all I have access to in MKAnnotationView is a UIImage. How would you guys tackle this?
I realize that #2 could be handled with a UIImageView on top of the map containing the animationImages. However, then I would have to manually handle translating the movement of the plane or rocket or whatever as the region of the mapview changed, depending on user movements in the real-world, or user zooming (scrolling is not allowed in my app).
What do you think?
I think I've figured out a solution to #2. I subclassed MKAnnotationView and wrote some code to add a UIImageView (with animation images) as a subview.
//AnimatedAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface AnimatedAnnotation : MKAnnotationView
{
UIImageView* _imageView;
NSString *imageName;
NSString *imageExtension;
int imageCount;
float animationDuration;
}
#property (nonatomic, retain) UIImageView* imageView;
#property (nonatomic, retain) NSString* imageName;
#property (nonatomic, retain) NSString* imageExtension;
#property (nonatomic) int imageCount;
#property (nonatomic) float animationDuration;
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier imageName:(NSString *)name imageExtension:(NSString *)extension imageCount:(int)count animationDuration:(float)duration
;
#end
//AnimatedAnnotation.m
#import "AnimatedAnnotation.h"
#implementation AnimatedAnnotation
#synthesize imageView = _imageView;
#synthesize imageName, imageCount, imageExtension,animationDuration;
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier imageName:(NSString *)name imageExtension:(NSString *)extension imageCount:(int)count animationDuration:(float)duration
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
self.imageCount = count;
self.imageName = name;
self.imageExtension = extension;
self.animationDuration = duration;
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:#"%#0.%#",name,extension]];
self.frame = CGRectMake(0, 0, image.size.width, image.size.height);
self.backgroundColor = [UIColor clearColor];
_imageView = [[UIImageView alloc] initWithFrame:self.frame];
NSMutableArray *images = [[NSMutableArray alloc] init];
for(int i = 0; i < count; i++ ){
[images addObject:[UIImage imageNamed:[NSString stringWithFormat:#"%#%d.%#", name, i, extension]]];
}
_imageView.animationDuration = duration;
_imageView.animationImages = images;
_imageView.animationRepeatCount = 0;
[_imageView startAnimating];
[self addSubview:_imageView];
return self;
}
-(void) dealloc
{
[_imageView release];
[super dealloc];
}
#end