here is my code :
-(void) createNewImage {
UIImage * image = [UIImage imageNamed:#"boutonplay_03.png"];
imageView = [[UIImageView alloc] initWithImage:image];
[imageView setCenter:[self randomPointSquare]];
[[self view] addSubview:imageView];
ix=imageView.center.x;
iy=imageView.center.y;
X=(240-ix)/230;
Y=(160-iy)/230;
}
-(void)Animation{
imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y);
}
"CreateNewImage" is alled every 2 seconds, and there is a CADisplayLink on "Animation".
My problem is that the animation of "imageView" stops every 2 seconds because a new "imageView" is created and this last start moving and it stops again after 2 seconds, and again and again and again ... What I would like is to make "imageView" continue his animation in spite of the creation of new "imageView".How can I do this please ? sorry for my english I'm french:/
Check to see if imageView == nil or not.
if (imageView == nil) {
imageView = [[UIImageView alloc] initWithImage:image];
}
//rest of code
You are creating a new UIImageView every time you call CreateNewImage
instead, check to see if it has already been created.
-(void) createNewImage {
if(imageView == nil) {
UIImage * image = [UIImage imageNamed:#"boutonplay_03.png"];
imageView = [[UIImageView alloc] initWithImage:image];
}
[imageView setCenter:[self randomPointSquare]];
[[self view] addSubview:imageView];
ix=imageView.center.x;
iy=imageView.center.y;
X=(240-ix)/230;
Y=(160-iy)/230;
}
If I understand correctly your case, what happens is this:
you create a new image in createNewImage;
this image is animated through the method:
-(void)Animation{
imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y);
}
which animates your imageView ivar;
when you create a new image, after 2 seconds, you reassign it to the imageView ivar, so that the Animation method can animate it when it is called;
at the same time, you have lost the reference to your previous image that you were animating before, so Animation will not animate it anymore.
Now what I suggest is trying to change Animation in this way:
-(void)Animation{
for (UIView* imgView in self.view.subviews) {
imgView.center = CGPointMake(imgView.center.x + X, imgView.center.y + Y);
}
}
So, this will simply animate any subview of your main view. If your only subviews are the images you want to animate, it should be fine.
EDIT:
From your description in the comments, it seems to me that what happens is that addSubview has the effect of stopping shortly all animations. I don't know if this is related to loading the images from disk, but I remember I also experienced a similar problem time ago. The only approach I see is pre-creating and pre-adding all the UIImageViews you need (or a maximum number of allowed images), make them all hidden and then making them visible again one by one. This would make the logic of your program more complex, but it would work.
Another approach could be using CALayers instead of UIVIews. Best thing would be using a CABasicAnimation to animate your images.
If you want to use UIImageView, you could get [imageView layer] and add this one instead of the view, like this:
[[self.view layer] addSublayer:[imageView layer]];
but I am not sure that this approach would work.
Hope that his helps.
Related
Background:
I am using x-code version 3.1.4 and an iphone simulator version 3.1.
Right now I am building a game where a car (portrayed by a UIImageView) moves left and right using two buttons (left and right). That part works fine. However, I am trying to randomly spawn UIImageViews at the top that will go down at a constant rate. I am able to get one UIImageView to move using this:
-(void)moveMeteor{
iv.center=CGPointMake(iv.center.x, iv.center.y + 1);
[self performSelector:#selector(moveMeteor) withObject:iv afterDelay:0.01];
}
where iv is a UIImageView and I call this function when I click a button. However, I want to create randomly spawning UIImageViews. So, I did this using this code:
-(void)addViews: (NSTimer *) aTimer {
UIImageView *iv = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Meteor.png"]];
CGRect rect = CGRectMake(arc4random() % (350), arc4random() % (5), 35, 35);
[iv setFrame:rect];
[self.view addSubview:iv];
}
I call this function by clicking a start button which does this:
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(addViews:) userInfo:nil repeats:YES];
I thought about using tags by changing the addViews function to :
-(void)addViews: (NSTimer *) aTimer {
UIImageView *iv = [[UIImageView alloc]initWithImage [UIImage imageNamed:#"Meteor.png"]];
CGRect rect = CGRectMake(arc4random() % (350), arc4random() % (5), 35, 35);
[iv setFrame:rect];
[self.view addSubview:iv];
iv.tag=tagStart;
[self moveMeteor:tagStart];
tagStart = tagStart+1;
}
(the initial value of tagStart is 1);
and changing the move function to:
-(void)moveMeteor:(int)tag{
UIImageView *test= (UIImageView *)[self.view viewWithTag:tagStart];
test.center=CGPointMake(test.center.x, test.center.y + 1);
[self performSelector:#selector(moveMeteor) withObject:tagStart afterDelay:0.01];
[test release];
I am very confused on how to resolve this issue. The problem is either that my move function cannot handle so many moving things at a time, my tag system is faulty, my *test is faulty, or that I am calling to many functions at the same time, or something else. Please help and give me a solution to this annoying problem.
A better option than performSelector is NSTimer, and a better option again is CADisplayLink. You limit of performance will probably be using UIImageView and the associated overhead with moving and redrawing them all, so you may want to change to use CALayers instead. But, change one thing at a time.
For the meteors, I'd hold them all in an array and then each time your moveMeteor method is called, iterate through the array and move each one. Your tag method could work but will be less efficient (you're missing a loop to iterate through the tags basically).
-(void)moveMeteors {
for (UIImageView *test in self.meteors) {
test.center = CGPointMake(test.center.x, test.center.y + 1);
}
}
If you have an array called meteors that you add your new meteors to, and a timer calls moveMeteors periodically.
I'm fairly new to Objective-C development.
In my first app built from scratch, I'd like to draw a circle that increases its size as time runs.
The problem is that I've been able to move the circle through the screen, but can't seem to update its properties so it gets bigger or smalles.
Thing goes like this:
CCircle class
#interface CCircle : CShapes
#property float radius;
#property float startAngleRadians;
#property float endAngleRadians;
In the first view controller (ShapesViewController.m):
- (void) viewDidLoad
{
(...)
CCircle* circle = [[CCircle alloc] initWithFrame:frame];
circle.radius = 40;
circle.startAngleRadians = M_PI;
circle.endAngleRadians = 2*M_PI;
circle.tag = 1;
[self.view addSubView:circle];
//I also schedule an update method, so that it is called every 1 seconds.
[NSTimer scheduledTimerWithTmieInterval:1.0 target:self selector:#selector(updateCircle) userInfo:nil repeats:YES];
}
Also in ShapesViewController.m, the update method:
- (void) updateCircle
{
CCircle *circle = [self.view viewWithTag:1];
//Now here: if I do this: the circle will "move" through the screen
CGRect frame = circle.frame;
frame.origin.x += 5;
[circle setFrame:frame];
//However, if I try to change the circle properties, I don't know what to do so
//that affects the circle. In this case, the circle will move through the screen,
//but I keep seeing always the same size(radius).
circle.radius += 5;
//I've tried the following (of course, not all at the same time):
//[self.vew addSubview:circle];
//[self.view sendSubviewToBack:circle];
//[self.view sendSubviewToFront:circle];
//[self.view setNeedsDisplay];
//[self.view setNeedsLayout];
}
Any help on what am I doing wrong, and what should I do to achieve what I want?
Thanks!
You updated a property of circle, not self.view, so you need to call setNeedsDisplay on circle.
After drawing the circle, you have to update your view.
[self.view setNeedsDisplay];
I think that'll do it.
Okay, I have a problem with my code. I want to make some sort of grid like game using imageViewers, but when the method that changes the pictures which the imageViewers how, it only updates the screen/imageviewers once it is finished executing, not when the line that does it is actually executed. I has a method with an infinite loop in it and it changes the pictures of the imageViewers as you press buttons. The buttons call methods which change a boolean variable to true, so the main game loop knows whats been pressed by constantly checking if certain variables are true. But because the game loop never ends the screen is never updated. Is there a way to update what's on the screen during execution?
I am using this code to change the imageViewers contents:
UIImage * white = [UIImage imageNamed: #"White.png"];
UIImage * blue = [UIImage imageNamed: #"Blue.png"];
int tagInt=1;
for (int y = 0;y<10;y++){
for (int x = 0;x<10;x++){
UIImageView *imageView;
imageView = [[UIImageView alloc] initWithImage:white]; imageView.frame =CGRectMake(x*32,y*37,32,37);
imageView.image = white;
imageView.tag=tagInt;
[self.view addSubview:imageView];
tagInt++;
}
}
for (int u = 1;u<20;u++){
[NSThread sleepForTimeInterval:(1)];
UIImageView *g = (UIImageView*)[self.view viewWithTag:u];
g.image =blue;
}
This is a contrived example, because my actual code is too long, but has the same problem. It only updates on-screen after the last for loop (this delays/thread-sleep commands) finishes, not as the lines of code execute.
EDIT: Here is the project if you want any context: https://www.dropbox.com/s/cvefllnhgj0tp6g/Stacker.zip
In iOS, all UI related stuff is done in the main (UI) thread. If you have an operation that blocks the ui thread - which means, it keeps the thread busy as you do - no view update will occur.
So you will have to change your code that way, that you have a "updateView" method which changes the image views' content. This method will have to be called on a regular base, e.g. by using an NSTimer instance: https://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/nstimer_Class/Reference/NSTimer.html
Assuming, the code to update your view is named "updateView", you could define a timer like this:
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector:#selector(updateView:)
userInfo: nil
repeats: YES];
So the timer would call your updateView method every second and would not block the main thread which again would allow the changes you made to the view to become visible.
I think a CA animation will do what you need. Try something like this:
#import <QuartzCore/QuartzCore.h>
- (void)imageView:(UIImageView *)imageView crossfadeTo:(UIImage *)image duration:(NSTimeInterval)duration {
[imageView.layer removeAnimationForKey:#"animateContents"];
CABasicAnimation *crossFade = [CABasicAnimation animationWithKeyPath:#"contents"];
crossFade.duration = duration;
crossFade.fromValue = (id)imageView.image.CGImage;
crossFade.toValue = (id)image.CGImage;
[imageView.layer addAnimation:crossFade forKey:#"animateContents"];
imageView.image = image;
}
This can also be made into a category method on UIImageView. Call it like this:
UIImage *white = [UIImage imageNamed: #"White.png"];
UIImage *blue = [UIImage imageNamed: #"Blue.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:white];
[self imageView:imageView crossfadeTo:blue duration:5.0];
Alternatively, this can be done with block animation calling it the same way, as follows:
- (void)imageView:(UIImageView *)imageView crossfadeTo:(UIImage *)image duration:(NSTimeInterval)duration {
UIImageView *fadeToImageView = [[UIImageView alloc] initWithFrame:imageViewFrame];
fadeToImageView.image = image;
[imageView.superview insertSubview:fadeToImageView belowSubview:imageView];
[UIView animateWithDuration:duration animations:^{
imageView.alpha = 0.0;
} completion:^(BOOL finished) {
imageView.image = image;
imageView.alpha = 1.0;
[fadeToImageView removeFromSuperview];
}];
}
I would like to know the best way/correct way to achieve from following layout? I want to place an UIImageView outside of UITableViewCell in a UITableView with static cells.
I have done the following by subclassing UITableViewCell for the cells in section 1 using the following code
- (void)setFrame:(CGRect)frame {
frame.size.width -= 125.0;
[super setFrame:frame];
}
In the UITableViewController's viewDidLoad I add the UIImageView and position it according to the width of the custom UITableViewCell.
This sort of works, but i'm not sure how to deal with rotation and also if what i've done so far would be the correct way?
there are differnt ways to do it. one is to set the width of table view less as you showd in pic 2nd is to use custom table view cell and on required cell add image so that your cell data as well as image will be shown. i think custom cell would be the better solution. tell me if you are asking the same thing what i answered, if no, then i review my answer thank.
I managed to produce what I wanted using the follow, this is proberly not the best way or cleanest way but as no one from StackOverFlow gave any better suggestions I thought I better answer this.
I subclassed the first 3 UITableViewCells and set a frame size to take into account the size of my image.
- (void)setFrame:(CGRect)frame {
float cellWidth = (frame.size.width - 345);
frame.size.width = cellWidth;
[super setFrame:frame];
}
Then in my UITableViewController's viewDidLoad I create and position the UIImageView using the first static cell in the tableview as an IBOutlet ("firstCell"). I then set the autoResizingMask which sorts out rotation and finally add the UIImageView to the view.
//Create and position the image view
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((firstCell.frame.size.width), (firstCell.frame.origin.y + 70), 300, 137)];
// Add border and round corners of image view to make style look a little like tableviewcells
[imageView.layer setBorderColor:[UIColor colorWithWhite:0 alpha:0.5].CGColor];
[imageView.layer setBorderWidth:2.0];
[imageView.layer setCornerRadius:5.0];
[imageView setClipsToBounds:YES];
//Set the image in the image view
UIImage *image = [UIImage imageNamed:#"defaultPicture.png"];
imageView.image = image;
//Set resizing of image view for when view is rotated
imageView.contentMode = UIViewContentModeRedraw;
imageView.autoresizingMask = UIViewContentModeScaleAspectFit;
//Add tap gesture for imageview to initiate taking picture.
imageView.userInteractionEnabled = YES;
UITapGestureRecognizer *imageViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(takePicture:)];
[imageView addGestureRecognizer:imageViewTap];
//Add image view to view
[self.view addSubview:imageView];
This has not been fully tested and i'm sure isn't a great implementation, but its a starting point for the effect i'm after. Please reply if you know of a better way.
Note: The above code is written for my app on the iPad but screenshots are from testing I did on iPhone
Basically, I want to have an app with only one view that has an image on it. I want to be able to swipe left or right and have the first image go out of the view and the second image to come in. The images are the same and I want it to look like they are connected (like scrolling down a rope where the pattern just repeats, but it looks like a constant scroll). I need it to be able to change the image or restart after a series of swipes. I know that I need to turn pagination ON in the UIScrollView, but I am new to iOS and am having trouble.
Ultimately, I want to have the iPhone vibrate every so-and-so swipes (and restart the pattern).
I'm sure that there are a lot of ways to do this (i.e. a TableView) so feel free to just point me in the direction of some references if the answer is tedious to explain.
Thanks!
FOLLOW UP:
I found an Apple example that did very nearly what I wanted to do. I made a lot of adjustments to it, but I'm banging my head against a wall trying to get the images to cycle. Here is what I think is the offending code, but I'm not sure what the solution is, as the ScrollView is functional, it just doesn't reset the center to the current view. Any ideas?
- (void)layoutScrollImages
{
UIImageView *view = nil;
NSArray *subviews = [scrollView1 subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UIImageView class]] && view.tag > 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
// set the content size so it can be scrollable
[scrollView1 setContentSize:CGSizeMake((kNumImages * kScrollObjWidth), [scrollView1 bounds].size.height)];
}
I'd just use a UIScrollView. Set the contentWidth to be 3 times the width/height of the view (for 3 pages) and set the contentOffset to be the center 'page' (view.bounds.size.width or view.bounds.size.height depending on whether you're scrolling horizontally/vertically respectively) . You'll need to setup a delegate for the UIScrollView (probably the view controller) and implement - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView. This will be called when the scroll view has finished decelerating. Once it has finished decelerating, reset the contentOffset back to the center view. This should give the impression of an infinite scroll. You can also set a counter to increment in the scrollViewDidEndDecelerating method to increment the counter or initiate the vibration.
You shouldn't need to keep repositioning the images. Just set the images once in the scrollView:
//Horizontal arrangement
UIImage *image = [UIImage imageNamed:#"nameOfImage.png"];
UIImageView *imageView1 = [[UIImageView alloc] initWithImage:image];
UIImageView *imageView2 = [[UIImageView alloc] initWithImage:image];
UIImageView *imageView3 = [[UIImageView alloc] initWithImage:image];
NSArray *imageViews = [NSArray arrayWithObjects:imageView1, imageView2, imageView3];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[self.view addSubview: scrollView]; //This code assumes it's in a UIViewController
CGRect cRect = scrollView.bounds;
UIImageView *cView;
for (int i = 0; i < imageViews.count; i++){
cView = [imageViews objectAtIndex:i];
cView.frame = cRect;
[scrollView addSubview:cView];
cRect.origin.x += cRect.size.width;
}
scrollView.contentSize = CGSizeMake(cRect.origin.x, scrollView.bounds.size.height);
scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0); //should be the center page in a 3 page setup
So the images are setup, you don't need to mess with them anymore. Just reset the contentOffset when the scroll views stops (note: you need to make sure you're the delegate of the scroll view or you'll not receive the message when the scroll view stops):
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
}
Please forgive any typos. I wrote it out by hand.
Look on cocoacontrols.com for a custom photo album view. As for the vibration, this code snippet vibrates the phone (make sure you link to and #import <AudioToolbox/AudioToolbox.h>):
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);