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
Related
I am doing some exercises with delegate, but now I have a problem with a UIView. This is my storyboard
I want to change the color of the UIView with 3 UISliders. The range of UISliders is from 0 to 255.
And this is my code:
ColorField is the UIView custom class
ColorField.h
#import <UIKit/UIKit.h>
#protocol ColorFieldDelegate <NSObject>
-(NSArray *)giveMeColors;
#end
#interface ColorField : UIView
#property (nonatomic , weak) IBOutlet id<ColorFieldDelegate> delegate;
#end
ColorField.m
#import "ColorField.h"
#implementation ColorField
#synthesize delegate = _delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
NSArray *arrayOfColors = [self.delegate giveMeColors];
int red = [[arrayOfColors objectAtIndex:0] intValue];
int green = [[arrayOfColors objectAtIndex:1] intValue];
int blue = [[arrayOfColors objectAtIndex:2] intValue];
NSLog(#"Red --> %d" ,red);
NSLog(#"Green --> %d" ,green);
NSLog(#"Blue --> %d \n\n" ,blue);
self.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}
#end
ColorViewController.h
#import <UIKit/UIKit.h>
#import "ColorField.h"
#interface ColorViewController : UIViewController <ColorFieldDelegate>
#property (nonatomic) IBOutlet ColorField *colorField;
#property (weak, nonatomic) IBOutlet UISlider *redSlider;
#property (weak, nonatomic) IBOutlet UISlider *greenSlider;
#property (weak, nonatomic) IBOutlet UISlider *blueSlider;
#end
ColorViewController.m
#import "ColorViewController.h"
#interface ColorViewController ()
#property (nonatomic) double redQuantity;
#property (nonatomic) double greenQuantity;
#property (nonatomic) double blueQuantity;
#end
#implementation ColorViewController
#synthesize colorField = _colorField;
#synthesize redSlider;
#synthesize greenSlider;
#synthesize blueSlider;
#synthesize redQuantity;
#synthesize blueQuantity;
#synthesize greenQuantity;
- (void)viewDidLoad
{
[super viewDidLoad];
[self.colorField setDelegate:self];
[self.colorField setNeedsDisplay];
self.redQuantity = 125.0;
self.blueQuantity = 125.0;
self.greenQuantity = 125.0;
[self.colorField setNeedsDisplay];
// Do any additional setup after loading the view, typically from a nib.
}
-(ColorField *)colorField
{
if (_colorField == nil) {
_colorField = [[ColorField alloc] init];
}
return _colorField;
}
- (void)viewDidUnload
{
[self setColorField:nil];
[self setRedSlider:nil];
[self setGreenSlider:nil];
[self setBlueSlider:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
-(IBAction)changeRedQuantity:(UISlider *)sender
{
//methods of UISliders
self.redQuantity = [sender value];
[self.colorField setNeedsDisplay];
}
-(IBAction)changeBlueQuantity:(UISlider *)sender
{
self.blueQuantity = [sender value];
[self.colorField setNeedsDisplay];
}
-(IBAction)changeGreenQuantity:(UISlider *)sender
{
self.greenQuantity = [sender value];
[self.colorField setNeedsDisplay];
}
-(NSArray *)giveMeColors
{
NSNumber *redNumber = [NSNumber numberWithDouble:self.redQuantity];
NSNumber *greenNumber = [NSNumber numberWithDouble:self.greenQuantity];
NSNumber *blueNumber = [NSNumber numberWithDouble:self.blueQuantity];
NSArray *array = [[NSArray alloc] initWithObjects:redNumber, greenNumber,blueNumber, nil];
return array;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
BUT...this is the result: It show me only RED, GREEN and BLUE colors without gradation, for example: RGB (255,0,75) is THIS
and not THIS:
I don't know with it can't show me gradation...
Thanks!!
Marco Manzoni
You have to divide your slidervalues by 255.0.
[UIColor colorWithRed:red/255.0 green:green/255.0 blue:blue/255.0 alpha:1.0];
colorWithRed only accepts values 0.0-1.0
I am writing a program with a UIImageView. However, I am running into the error: Exc_Arithmetic.
Here is my implementation:
#import "SalaatAppViewController.h"
#implementation SalaatAppViewController
#synthesize imageView;
#synthesize pauseButton;
#synthesize playButton;
#synthesize nextButton;
-(IBAction)pushPauseButton {
[imageView stopAnimating];
}
-(IBAction)pushPlayButton {
[imageView startAnimating];
}
- (IBAction)pushNextButton {
imagesIndex = (imagesIndex + 1) % [images count];
imageView.image = [images objectAtIndex:imagesIndex];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
imageView.animationImages = [[NSArray arrayWithObjects:
[UIImage imageNamed:#"1.png"],
[UIImage imageNamed:#"2.png"],
[UIImage imageNamed:#"3.png"],
nil] retain];
UIImage *image = [UIImage imageNamed: #"2.png"];
[imageView setImage:image];
imageView.animationDuration = 1.00;
imageView.animationRepeatCount = 0;
[imageView startAnimating];
imagesIndex = 0;
imageView.image = [images objectAtIndex:imagesIndex];
[super viewDidLoad];
[self.view addSubview:imageView];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(void) dealloc {
[imageView release];
[pauseButton release];
[playButton release];
[nextButton release];
[super dealloc];
}
#end
My Header:
#import <UIKit/UIKit.h>
#interface SalaatAppViewController : UIViewController {
UIImageView *imageView;
UIButton *pauseButton;
UIButton *playButton;
UIButton *nextButton;
NSUInteger images;
NSUInteger imagesIndex;
}
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
#property (nonatomic, retain) IBOutlet UIButton *pauseButton;
#property (nonatomic, retain) IBOutlet UIButton *playButton;
#property (nonatomic, retain) IBOutlet UIButton *nextButton;
-(IBAction)pushPauseButton;
-(IBAction)pushPlayButton;
-(IBAction)pushNextButton;
#end
The program runs, but with 9 warnings and 1 error. Also, when I push the Next Button, it crashes. Please Help!
The exception indicates that you tried to divide by 0. This expression does a modulus (%) function which returns remainder after division, probably [images count] is 0:
imagesIndex = (imagesIndex + 1) % [images count];
Ahmad your "images" property should be not NSUInteger but NSArray*. Define it in a following way:
NSArray *images;
It seems you replaced too much by following my previous guidance. =)
You never initialize images. Also, as you're calling the count method, it seems that it should be an NSArray and not an NSUInteger.
So, I am trying to make a shared method in my Brain class which will check the label on a pressed button (buttons of items are in different classes) and then accordingly add a image to a ShoppingList view minding the order of adding (first image added goes to position 0,0, second to position 80,0 etc.).
I have Breakfast class which represents breakfast ingredients and I wanna add a photo of a Tea to another view called ShoppingList.
I wrote a method in Brain that adds an image to a imageView and returns an imageView which is then locally passed to the button pressed.
It builds but when I press the button Tea, application crashes.
Here is my code:
Brain.h
#interface Brain : NSObject {
#private
UIImage *image;
UIImageView *imageView;
}
#property (retain) UIImageView *imageView;
#property (retain) UIImage *image;
- (id)performOperation:(NSString *)operation;
#end
Brain.m
#implementation Brain
#synthesize imageView;
#synthesize image;
- (UIImageView *)performOperation:(NSString *)operation
{
if ([operation isEqual:#"Tea"]) {
image = [UIImage imageNamed:#"Tea_photo.jpg"];
imageView = [[UIImageView alloc]initWithImage:image];
imageView.frame = CGRectMake(0, 0, 80, 80);
return imageView;
//[shoppingList.view addSubview:imageView];
//[imageView release];
}
else return 0;
}
#end
Breakfast.h
#interface Breakfast : UIViewController {
IBOutlet ShoppingList *shoppingList;
Brain *brain;
}
- (IBAction)addItemToShoppingList:(UIButton *)sender;
- (IBAction)goToShoppingList;
- (IBAction)goBack;
#end
Breakfast.m
#implementation Breakfast
- (Brain *)brain
{
if (!brain) brain = [[Brain alloc] init];
return brain;
}
- (IBAction)addItemToShoppingList:(UIButton *)sender
{
NSString *operation = [[sender titleLabel] text];
UIImageView *imageView = [[self brain] performOperation:operation];
[shoppingList.view addSubview:self.brain.imageView];
//UIImageView *imageView = [[UIImageView alloc]initWithImage:image];
//imageView.frame = CGRectMake(0, 0, 80, 80);
//[shoppingList.view addSubview:imageView];
//[imageView release];
}
- (IBAction)goToShoppingList
{
[self presentModalViewController:shoppingList animated:NO];
}
- (IBAction)goBack
{
[self dismissModalViewControllerAnimated:NO];
}
#end
PLEASE HELP, its for my thesis.
IBOutlet ShoppingList *shoppingList;
Why make this IBOutlet? Isn't ShoppingList a class that you created. Not that this solves your crash. For that u need to post the crash log....
And also in you code I can't see any allocation for shoppingList so how can you be able to use it. You need to allocate the object in the class Brain otherwise it doesn't make any sense.
I have not gone through your code. But on the basis of your description I can suggest you to create a protocol
Given the multitasking of iOS I thought it wouldn't be a pain to pause and resume my app, by pressing the home button or due to a phone call, but for a particular view controller it crashes.
The navigation bar is working fine i.e. when I tap "Back" it's ok, but if I try to tap controls of the displayed UI View Controller then I get EXC_BAD_ACCESS.. =/
Please find the code of my problematic View Controller below. I'm not very sure about it myself, because I used loadView to build its View. However apart from this interruption problem it works fine.
StoreViewController.h
#import <UIKit/UIKit.h>
#protocol StoreViewDelegate <NSObject>
#optional
- (void)DirectionsClicked:(double)lat:(double)lon;
#end
#interface StoreViewController : UIViewController
{
double latitude;
double longitude;
NSString *description;
NSString *imageURL;
short rating;
NSString *storeType;
NSString *offerType;
UIImageView *imageView;
UILabel *descriptionLabel;
id<StoreViewDelegate> storeViewDel;
}
#property (nonatomic) double latitude;
#property (nonatomic) double longitude;
#property (nonatomic) short rating;
#property (nonatomic,retain) NSString *description;
#property (nonatomic,retain) NSString *imageURL;
#property (nonatomic,retain) NSString *storeType;
#property (nonatomic,retain) NSString *offerType;
#property (assign) id<StoreViewDelegate> storeViewDel;
#end
StoreViewController.m
#import "StoreViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface StoreViewController()
-(CGSize) calcLabelSize:(NSString *)string withFont:(UIFont *)font maxSize:(CGSize)maxSize;
#end
#implementation StoreViewController
#synthesize storeViewDel, longitude, latitude, description, imageURL, rating, offerType, storeType;
- (void)mapsButtonClicked:(id)sender
{
}
- (void)loadView
{
UIView *storeView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, 0.0f)];
// Colours
UIColor *lightBlue = [[UIColor alloc] initWithRed:(189.0f / 255.0f)
green:(230.0f / 255.0f)
blue:(252.0f / 255.0f) alpha:1.0f];
UIColor *darkBlue = [[UIColor alloc] initWithRed:(28.0f/255.0f)
green:(157.0f/255.0f)
blue:(215.0f/255.0f)
alpha:1.0f];
// Layout
int width = self.navigationController.view.frame.size.width;
int height = self.navigationController.view.frame.size.height;
float firstRowHeight = 100.0f;
int margin = width / 20;
int imgWidth = (width - 3 * margin) / 2;
// Set ImageView
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(margin, margin, imgWidth, imgWidth)];
CALayer *imgLayer = [imageView layer];
[imgLayer setMasksToBounds:YES];
[imgLayer setCornerRadius:10.0f];
[imgLayer setBorderWidth:4.0f];
[imgLayer setBorderColor:[lightBlue CGColor]];
// Load default image
NSData *imageData = [NSData dataWithContentsOfFile:#"thumb-null.png"];
UIImage *image = [UIImage imageWithData:imageData];
[imageView setImage:image];
[storeView addSubview:imageView];
// Set Rating
UIImageView *ratingView = [[UIImageView alloc] initWithFrame:CGRectMake(3 * width / 4 - 59.0f,
margin,
118.0f,
36.0f)];
UIImage *ratingImage = [UIImage imageNamed:#"bb-rating-0.png"];
[ratingView setImage:ratingImage];
[ratingImage release];
[storeView addSubview:ratingView];
// Set Get Directions button
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(3 * width / 4 - 71.5f,
36.0f + 2*margin,
143.0f, 63.0f)];
[btn addTarget:self action:#selector(mapsButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
UIImage *mapsImgUp = [UIImage imageNamed:#"bb-maps-up.png"];
[btn setImage:mapsImgUp forState:UIControlStateNormal];
[mapsImgUp release];
UIImage *mapsImgDown = [UIImage imageNamed:#"bb-maps-down.png"];
[btn setImage:mapsImgDown forState:UIControlStateHighlighted];
[mapsImgDown release];
[storeView addSubview:btn];
[btn release];
// Set Description Text
UIScrollView *descriptionView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f,
imgWidth + 2 * margin,
width,
height - firstRowHeight)];
descriptionView.backgroundColor = lightBlue;
CGSize s = [self calcLabelSize:description withFont:[UIFont systemFontOfSize:18.0f] maxSize:CGSizeMake(width, 9999.0f)];
descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(margin, margin, width - 2 * margin, s.height)];
descriptionLabel.lineBreakMode = UILineBreakModeWordWrap;
descriptionLabel.numberOfLines = 0;
descriptionLabel.font = [UIFont systemFontOfSize:18.0f];
descriptionLabel.textColor = darkBlue;
descriptionLabel.text = description;
descriptionLabel.backgroundColor = lightBlue;
[descriptionView addSubview:descriptionLabel];
[storeView addSubview:descriptionView];
[descriptionLabel release];
[lightBlue release];
[darkBlue release];
self.view = storeView;
[storeView release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc
{
[imageView release];
[descriptionLabel release];
[super dealloc];
}
#end
Any suggestions ?
Thank you,
F.
Perhaps look at your crash report, get an idea of where it's crashing, and help us help you by posting the relevant code if it is not clear to you what is causing the crash. We may be able to help you further, but we need you to do some pre-requisite work first, like trying to solve it yourself with the appropriate information at hand. Now that you know what to look for, please, go forth and unicorn!
The problem was that I released some UIImages that were never allocated. Erasing the following lines solved my issue:
[ratingImage release];
[mapsImgUp release];
[mapsImgDown release];
F.
I have a view that only has a button UIButton. First time clicking the button will draw a square above the button and the second time will replace the square with a circle.
I need some help on writing the controller code. Please guide me to the right path. Thanks
design a shape class. Add your shapes as UIImageViews. with each button click, change the alpha on click.
#interface Shape: UIView {
UIImageView *square;
UIimageView *circle;
BOOL isSquare;
}
#property (nonatomic, retain) UIImageView *square;
#property (nonatomic, retain) UIImageView *circle;
#property BOOL isSquare;
-(void)changeShape;
#end
#implementation Shape
#synthesize square;
#synthesize circle;
#synthesize isSquare;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.frame = //frame size.
//assume a square image exists.
square = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"square.png"]];
square.alpha = 1.0;
[self addSubview: square];
//assume a circle image exists.
circle = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"circle.png"]];
circle.alpha = 0.0;
[self addSubview: circle];
isSquare = YES;
}
return self;
}
-(void)changeShape {
if (isSquare == YES) {
square.alpha = 0.0;
cirle.alpha = 1.0;
isSquare = NO;
}
if (isSquare == NO) {
circle.alpha = 0.0;
square.alpha = 1.0;
isSquare = YES;
}
}
//rls memory as necessary.
#end
////////////////////////////////in your view controller class.
instantiate your class and add it as a subview.
#class Shape;
#interface ShapeViewController : UIViewController {
Shape *shape;
}
#property (nonatomic, retain) Shape *shape;
-(IBAction)clickedButton:(id)sender;
#end
////////
#import "Shape.h"
#implementation ShapeViewController;
//add your shape as a subview in viewDidLoad:
-(IBAction)clickedButton:(id)sender {
[shape changeShape];
}
#end
That should give you a good basic implementation idea. Essentially, you design a class and then change its shape with each click. It should alternate from square/circle/square/circle. Hope that helps.