Objective C: Passing variables between classes - objective-c

I want to pass an NSMutableDictionary from one class through a timer to another class that is called from the timer. I get an error at #selector([generateStickFig:enemies]) I've tried taking [] out but it still errs.
-(void)awakeFromNib {
//start timer that fires every second
NSMutableDictionary *enemies = [[NSMutableDictionary alloc] init];
[NSTimer scheduledTimerWithTimeInterval:(0.001) target:self selector:#selector([generateStickFig:enemies]) userInfo:nil repeats:YES];
}
-(void) generateStickFig:(NSMutableDictionary *)enemies {
int x = random() % 1000;
if (x == 1) {
stickFig = [[UIButton alloc] initWithFrame:CGRectMake(0, 650, 50, 50)];
[stickFig setBackgroundColor:[UIColor blackColor]];
[stickFig addTarget:self action:#selector(tapFig:) forControlEvents:UIControlEventTouchUpInside];
[enemies setObject:object forKey:[NSString stringWithFormat:#"object%i",i]];
[self.view addSubview:stickFig];
}
}
Any suggestions?

Try
[NSTimer scheduledTimerWithTimeInterval:(0.001) target:self selector:#selector(generateStickFig:) userInfo:enemies repeats:YES];
to create the timer. Then change the callback method to
-(void) generateStickFig:(NSTimer *)timer {
NSDictionary *enemies = (NSDictionary *)timer.userInfo;
...
}

Related

How to remove subclass of UILabel in itself

A subclass of UILabel called NewLabel, in NewLabel.m
+ (NewLabel*)addLabelIntoView:(UIView*)view
{
NewLabel *label = [[NewLabel alloc] init];
CGSize size = CGSizeMake(120.0f, 40.0f);
CGPoint point = CGPointMake(view.bounds.size.width / 2, view.bounds.size.height / 2);
label.frame = CGRectMake(0, 0, size.width, size.height);
label.center = point;
[view addSubview:label];
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(removeLabelFromView) userInfo:nil repeats:NO];
return label;
}
- (void)removeLabelFromView {
[self removeFromSuperview];
}
and in a UIViewController.m, I add this for showing the NewLabel, and close itself
[NewLabel addLabelIntoView:self.view]
but it always crash, here is crash info
+[NewLabel removeLabelFromView]: unrecognized selector sent to class 0x103d40738
Any ideas?
In a class method is self the class. Instead of +[NewLabel removeLabelFromView] you probably want to do -[NewLabel removeLabelFromView]. Change
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(removeLabelFromView) userInfo:nil repeats:NO];
to
[NSTimer scheduledTimerWithTimeInterval:1.0f target:label selector:#selector(removeLabelFromView) userInfo:nil repeats:NO];

how to create an action for a clicked image from imageviewanimation

[SOLVED - SEE THE ANSWER]
i want to have three images grouping as animation, and i can be able to click any of these three images to define an action. In the other words, i'd like to know the ways to create separate action/ event for each image in the animation
that will be great if you know there's a way to do with UIButton instead of UIImageview
here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
// Load images
NSArray *imageNames = #[#"hello.png",#"bye.png",#"helloagain.png"];
NSMutableArray *images = [[NSMutableArray alloc] init];
for (int i = 0; i < imageNames.count; i++) {
[images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
// Normal Animation
UIImageView *animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 95, 86, 90)];
animationImageView.animationImages = images;
animationImageView.animationDuration = 1.5;
[self.view addSubview:animationImageView];
[animationImageView startAnimating];
animationImageView.userInteractionEnabled = TRUE;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(bannerTapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[animationImageView addGestureRecognizer:singleTap];
}
- (void)bannerTapped:(UIGestureRecognizer *)gestureRecognizer {
//action here
}
You don't want to use an animating image view to do this, since you can't get the image from the image view when you click on it. Instead, use a timer to switch the images. Something like this should work,
#interface ViewController ()
#property (strong,nonatomic) NSMutableArray *images;
#property (strong,nonatomic) UIImageView *animationImageView;
#end
#implementation ViewController {
NSInteger counter;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *imageNames = #[#"hello.png",#"bye.png",#"helloagain.png"];
self.images = [[NSMutableArray alloc] init];
for (int i = 0; i < imageNames.count; i++) {
[self.images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
self.animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 95, 86, 90)];
self.animationImageView.image = self.images[0];
[self.view addSubview:self.animationImageView];
self.animationImageView.userInteractionEnabled = TRUE;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(bannerTapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self.animationImageView addGestureRecognizer:singleTap];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(changeImage:) userInfo:nil repeats:YES];
}
-(void)changeImage:(NSTimer *) timer {
if (counter == self.images.count - 1 ) {
counter = 0;
}else {
counter ++;
}
self.animationImageView.image = self.images[counter];
}
- (void)bannerTapped:(UIGestureRecognizer *)gestureRecognizer {
UIImage *img = self.animationImageView.image;
NSInteger indx = [self.images indexOfObject:img];
NSLog(#"The index is: %ld", indx);
// use indx value in a switch statement or if-else block to choose your action
}
After Edit
If you want different times between images, I would use a different approach. Instead of using a timer, just call the following method to start the animation,
-(void)changeImage {
if (counter == self.images.count - 1 ) {
counter = 0;
}else {
counter ++;
}
self.animationImageView.image = self.images[counter];
if (counter == 1) {
[self performSelector:#selector(changeImage) withObject:nil afterDelay:2];
}else{
[self performSelector:#selector(changeImage) withObject:nil afterDelay:1];
}
}
This would give you a 2 second delay between the 2nd and 3rd image, but 1 second between the others.
My proposed hacky solution assuming you know number of images, and animation time.
1. Schedule a timer for imageCount/animationTime milliseconds.
2. In the timer's callback, increment the above property on the UIImageView
3. In -bannerTapped use that index to decide what event to throw corresponding to the image currently being animated.
-(void)setup {
// Load images
NSArray *imageNames = #[#"happy",#"sad",#"popeye_village"];
NSMutableArray *images = [[NSMutableArray alloc] init];
for (int i = 0; i < imageNames.count; i++) {
[images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
// Normal Animation
imageview.frame = CGRectMake(60, 95, 86, 90);
imageview.animationImages = images;
imageview.animationDuration = 6;
[imageview startAnimating];
imageview.userInteractionEnabled = TRUE;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(bannerTapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[imageview addGestureRecognizer:singleTap];
[NSTimer scheduledTimerWithTimeInterval:((double)imageview.animationDuration / images.count)
target:self
selector:#selector(updateAnimatingImage)
userInfo:nil
repeats:YES];
}
- (void)bannerTapped:(UIGestureRecognizer *)gestureRecognizer
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:#"Clicked on Image index %li", imageview.indexOfAnimatingImage] message:#"" delegate:nil cancelButtonTitle:#"Cancel" otherButtonTitles: nil];
[alert show];
}
-(void)updateAnimatingImage
{
imageview.indexOfAnimatingImage = ++imageview.indexOfAnimatingImage % (imageview.animationImages.count);
NSLog(#"index currently showing is %li",imageview.indexOfAnimatingImage);
}
While very similar to the above solution, this still uses UIImageView's animationImages.
Here is my fork of your project in case you want to play with a working solution.

Display an image for 1 sec on the view

What I want to realize is:
When I touch a button, the image display on the view for 1 sec, then the image disappear.
I know that a NSTimer will help, but i dont know how to write the right code...need your help, thanks.
- (IBAction)bodytouched:(id)sender {
bodytouchedimage.hidden = NO;
bodytouchedimage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"beated.jpg"]];
bodytouchedimage.userInteractionEnabled = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(showPictures:) userInfo:nil repeats:NO];
}
- (void)showPictures:(NSTimer *)timer {
bodytouchedimage.hidden = YES;
}
What you should to is call the showPictures function when you touch the button and then within the showPictures method you add a NSTimer that will call a hidePictures method 1 second later
- (void)showPictures
{
bodytouchedimage.hidden = NO;
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(hidePictures) userInfo:nil repeats:NO];
}
- (void)hidePictures
{
bodytouchedimage.hidden = YES;
}
Rather than using NSTimer, it would be easier to simply call your method to hide the image like this:
- (IBAction)bodytouched:(id)sender {
bodytouchedimage.hidden = NO;
bodytouchedimage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"beated.jpg"]];
bodytouchedimage.userInteractionEnabled = YES;
[self performSelector:#selector(hidePicture) withObject:nil afterDelay:1];
}
- (void)hidePicture {
bodytouchedimage.hidden = YES;
}
performSelector:withObject:afterDelay: is a method of the NSObject class.

Pausing between image views

I want to run a series of pictures with dialog bubble (like a strip cartoon). I've tried to use the action:
-(IBAction) runDialog:(id)sender {
imageView.image = [UIImage imageNamed:#"a1.png"];
[NSThread sleepForTimeInterval:5.0];
imageView.image = [UIImage imageNamed:#"b1.png"];
[NSThread sleepForTimeInterval:5.0];
imageView.image = [UIImage imageNamed:#"b2.png"];
[NSThread sleepForTimeInterval:5.0];
imageView.image = [UIImage imageNamed:#"a2.png"];
[NSThread sleepForTimeInterval:5.0];
}
This doesn't work. All it does is show the last image (a2.png) After About 20 seconds
Any ideas how I should go about this:showing a series of pictures with a pause in between?
Use NSTimer instead.
- (IBAction)runDialog:(id)sender {
yourInstanceVariableOfNSTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f
target:self selector:#selector(showNextPage) userInfo:nil repeats:NO];
}
- (void)showNextPage {
imageView.image = [yourInstanceVariableOfNSArray
objectAtIndex:++yourInstanceVariableOfNSInteger];
if (!transitionsFinished) {
yourInstanceVariableOfNSTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f
target:self selector:#selector(showNextPage) userInfo:nil repeats:NO];
}
}
A simpler way to handle this would be to use a UIImageView in the dialog and set its animationImages, animationDuration, etc... properties.

Cocoa Touch - Looping Help

I need help with some programming logic... I need to loop this method to display the math problem in my labels then sleep(5) and then loop again. Anything I've tried ends of freezing the program. PLEASE help! I've been tearing my hair out trying everything I know!
EDIT: I edited the code to this, After 3 seconds it fades away the label displaying the problem but then it crashed and the debugger displays 2010-08-06 10:43:27.776 Reactor [13444:207] modifying layer that is being finalized - 0x5c13500
//quickfire problems
-(void)simpleMath{ //"Tap when the answer is X"
isTimeToTap = NO;
int problemSelector = arc4random() % 4;
NSArray *mathProblems = [[NSArray alloc] initWithObjects:#"6 - 1",#"2 + 3",#"3 x 2",#"3 x 1",#"2 x 4",nil]; //correct index 2
NSArray *mathAnswers = [[NSArray alloc] initWithObjects:#"5",#"5",#"6",#"3",#"8",nil]; //correct index 2
if ([mathAnswers objectAtIndex:problemSelector] == #"6") {
isTimeToTap = YES;
}
if (ranBefore == NO) { //create labels
//tell when to tap
tapTellerTop.text = #"Tap when the answer is 6!";
tapTellerBottom.text = #"Tap when the answer is 6!";
//make bottom label
mathDisplayBottom = [[UILabel alloc] initWithFrame:CGRectMake(15, 250, 242, 92)];
mathDisplayBottom.font = [UIFont fontWithName:#"Helvetica" size: 96.0];
mathDisplayBottom.textColor = [UIColor whiteColor];
mathDisplayBottom.backgroundColor = [UIColor clearColor];
[self.view addSubview: mathDisplayBottom];
//make top label
mathDisplayTop = [[UILabel alloc] initWithFrame:CGRectMake(55, 120, 242, 92)];
mathDisplayTop.font = [UIFont fontWithName:#"Helvetica" size: 96.0];
mathDisplayTop.textColor = [UIColor whiteColor];
mathDisplayTop.backgroundColor = [UIColor clearColor];
[self.view addSubview: mathDisplayTop];
//rotate top label
mathDisplayTop.transform = CGAffineTransformMakeRotation(180.0 /180.0 * M_PI);
}
//if ran before just update the text
mathDisplayBottom.text = [mathProblems objectAtIndex:problemSelector];
mathDisplayTop.text = [mathProblems objectAtIndex:problemSelector];
ranBefore = YES; //if its here. its been ran.
//run timer wait for (3) then loop again until userTaps = YES
[self performSelector:#selector(simpleMath) withObject:nil afterDelay:3.0f];
[mathProblems release];
[mathAnswers release];
[mathDisplayBottom release];
[mathDisplayTop release];
}
Sleep will stop your application from running or responding at all. Here is a quick partial example of a view controller you could use. This assumes you wire up the tap button and only uses one of the labels, etc. but you can play around with it. Also, this won't deal with memory issues or anything, so you'd add support for that.
But, once this view controller is created and the view installed, the math problem will update every 5 seconds. If the user presses the button and the answer is valid, we log a success message, otherwise we log fail message.
#interface myMathController : UIViewController {
NSArray* mathProblems;
NSIndexSet* validIndexes;
NSUInteger currentIndex;
NSTimer* timer;
}
#property(nonatomic,assign) IBOutlet UILabel* mathDisplayLabel;
- (void)updateProblem:(NSTimer*)timer;
- (IBAction)userTap;
#end
#implementation myMathController
- (void)viewDidLoad
{
[super viewDidLoad];
mathProblems = [[NSArray alloc] initWithObjects:#"6 - 1",#"2 + 3",#"3 x 2",#"3 x 1",#"2 x 4",nil];
// Add all valid answers
NSMutableIndexSet* tempIndexes = [[NSMutableIndexSet alloc] init];
[tempIndexes addIndex:2];
validIndexs = [tempIndexes copy];
[tempIndexes release];
timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:5.0 target:self selector:#selector(updateProblem:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)updateProblem:(NSTimer*)timer
{
currentIndex = arc4random() % [mathProblems count];
[mathDisplayLabel setText:[mathProblems objectAtIndex:currentIndex]];
}
- (void)userTap
{
if( [validIndexes containsIndex:currentIndex] ) {
NSLog(#"This user is SMART!");
} else {
NSLog(#"This user needs more work!");
}
}
- (void)dealloc
{
[timer invalidate];
[timer release];
[mathProblems release];
[validIndexes release];
[super dealloc];
}
#end
You shouldn't sleep(), ever. To invoke a function repeatedly, call:
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(doStuff:)
userInfo:nil repeats:YES];
then define the function:
-(void)doStuff:(NSTimer*)timer
{
// stuff
if ( iAmDone ) [timer invalidate];
}
OR if you want to fire another call, after 5 seconds, you could call
if ( !iAmDone ) [self performSelector:#selector(simpleMath) afterDelay:5];
at the end of simpleMath.