I'm making my second game on XCode and there seems to be something wrong with the code. It's a space shooter game where the playership follows your finger and you tap to release the missile. The problem is... when I press 'start game', everything is hidden and will not popup. Here is my viewcontroller.h and viewcontroller.m
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
int score;
int lives;
int enemyAttackOccurence;
int enemyPosition;
int randomSpeed;
float enemySpeed;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
#implementation PlayViewController
-(void)viewDidAppear:(BOOL)animated {
// Images that are to be hidden
playerShip.hidden = YES;
enemyShip.hidden = YES;
missile.hidden = YES;
earth.hidden = YES;
// Hidden Labels
scoreLabel.hidden = YES;
livesLabel.hidden = YES;
// Set score and lives remaining
score = 0;
lives = 0;
// Strings
scoreString = [NSString stringWithFormat:#"Score: 0"];
liveString = [NSString stringWithFormat:#"Lives: 0"];
// Initial Label Text
scoreLabel.text = scoreString;
livesLabel.text = liveString;
// Image starting positions
playerShip.center = CGPointMake(150, 658);
enemyShip.center = CGPointMake(175, 20);
missile.center = CGPointMake(playerShip.center.x, playerShip.center.y);
}
-(IBAction)startGame:(id)sender {
// Hide buttons
startButton.hidden = YES;
exitButton.hidden = YES;
// Images to show
playerShip.hidden = NO;
enemyShip.hidden = NO;
earth.hidden = NO;
// Labels
scoreLabel.hidden = NO;
livesLabel.hidden = NO;
[self positionEnemy];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
playerShip.center = CGPointMake(point.x, playerShip.center.y);
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[missileMovementTimer invalidate];
missile.hidden = NO;
missile.center = CGPointMake(playerShip.center.x, playerShip.center.y);
missileMovementTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(missileMovement) userInfo:nil repeats:YES];
}
-(void)positionEnemy {
// Random enemy position
enemyPosition = arc4random() % 249;
enemyPosition = enemyPosition + 20;
// Enemy Image Location
enemyShip.center = CGPointMake(enemyPosition, -40);
// Set enemy speed
randomSpeed = arc4random() % 3;
switch (randomSpeed) {
case 0:
enemySpeed = 0.03;
break;
case 1:
enemySpeed = 0.02;
break;
case 2:
enemySpeed = 0.01;
default:
break;
}
enemyAttackOccurence = arc4random() % 5;
[self performSelector:#selector(enemyMovementTimerMethod) withObject:nil afterDelay:enemyAttackOccurence];
}
-(void)enemyMovementTimerMethod {
enemyMovementTimer = [NSTimer scheduledTimerWithTimeInterval:enemySpeed target:self selector:#selector(enemyMovement) userInfo:nil repeats:YES];
}
-(void)enemyMovement {
enemyShip.center = CGPointMake(enemyShip.center.x, enemyShip.center.y + 2);
if (CGRectIntersectsRect(enemyShip.frame, earth.frame)) {
lives = lives - 1;
liveString = [NSString stringWithFormat:#"Lives: %i", lives];
livesLabel.text = liveString;
// Stop Enemy Moving
[enemyMovementTimer invalidate];
if (lives > 0) {
[self positionEnemy];
}
if (lives == 0) {
[self gameOver];
}
}
}
-(void)missileMovement {
missile.hidden = NO;
missile.center = CGPointMake(missile.center.x, missile.center.y - 2);
if (CGRectIntersectsRect(missile.frame, enemyShip.frame)) {
score = score + 1;
scoreString = [NSString stringWithFormat:#"Score: %i", score];
scoreLabel.text = scoreString;
// Stop missile
[missileMovementTimer invalidate];
// Position missile to be at the playerShip's center
missile.center = CGPointMake(playerShip.center.x, playerShip.center.y);
missile.hidden = YES;
// Stop enemy movement
[enemyMovementTimer invalidate];
[self positionEnemy];
}
}
-(void)gameOver {
[enemyMovementTimer invalidate];
[missileMovementTimer invalidate];
[self performSelector:#selector(gameReplay) withObject:nil afterDelay:3];
}
-(void) gameReplay {
// Images that are to be hidden
playerShip.hidden = YES;
enemyShip.hidden = YES;
missile.hidden = YES;
earth.hidden = YES;
// Hidden Labels
scoreLabel.hidden = YES;
livesLabel.hidden = YES;
// Set score and lives remaining
score = 0;
lives = 0;
// Strings
scoreString = [NSString stringWithFormat:#"Score: 0"];
liveString = [NSString stringWithFormat:#"Lives: 0"];
// Initial Label Text
scoreLabel.text = scoreString;
livesLabel.text = liveString;
// Image starting positions
playerShip.center = CGPointMake(150, 658);
enemyShip.center = CGPointMake(175, 20);
missile.center = CGPointMake(playerShip.center.x, playerShip.center.y);
}
#end
ViewController.h (Just for backup)
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
IBOutlet UIButton *startGame;
}
#end
#interface PlayViewController : UIViewController {
IBOutlet UIImageView *playerShip;
IBOutlet UIImageView *enemyShip;
IBOutlet UIImageView *missile;
IBOutlet UIImageView *earth;
IBOutlet UILabel *livesLabel;
IBOutlet UILabel *scoreLabel;
IBOutlet UIButton *startButton;
IBOutlet UIButton *exitButton;
UITouch *touch;
NSString *liveString;
NSString *scoreString;
NSTimer *enemyMovementTimer;
NSTimer *missileMovementTimer;
}
-(IBAction)startGame:(id)sender;
#end
I am watching a tutorial for this game, the person who created doesn't reply. Please help -- I cannot be any more specific. It just must be a weird gap in the code. Thanks.
Also, you have an IBOutlet and IBAction set for your StartGame button. The IBOutlet you never seem to use. You could be confusing your compiler by having the same name for the UIButton's IBOutlet and IBAction. Remove the IBOutlet, or change the name properly and see if that changes anything.
I'd recommend messing with your lines of code where you are setting object.hidden = YES and object.hidden = NO and see what happens. Often times tampering and testing your code is a good way to see what is going on. Make sure images are set for your UIImageViews. I'm assuming they are set in your interface builder because i don't see where you set them in your code. If there is no image set for the UIImageViews they will be see-through unless given a specific color. If tampering with the code doesn't work it won't hurt to re-watch the tutorial and make sure you didn't mess anything up. Often times the tutorials we watch are out-dated and we are left to solve a small problem ourselves and this may or may not be one of those instances. Again though, test your code and see if things are actually being set to hidden or not when you press that button.
Related
i am making an app i which i am using slider and on changing the position of slider value of slider is also sliding as well, now i am stuck that i want to set when i change postion of slider in horizontall form then i need to change that popover postion also in horizontall form. I am attaching a screen shot so that u people better understand it and this is my project link https://www.dropbox.com/s/n0fl34h6t8jlazn/CustomSlider.zip?dl=0,
below is my sample code on 1 view and i am changing my uislider class to anpopoverslider:
-(void)constructSlider {
_popupView = [[ANPopoverView alloc] initWithFrame:CGRectZero];
_popupView.backgroundColor = [UIColor clearColor];
_popupView.alpha = 0.0;
[self addSubview:_popupView];
}
and below is my ANpopoverslider
#import "ANPopoverSlider.h"
#implementation ANPopoverSlider
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self constructSlider];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self constructSlider];
}
return self;
}
#pragma mark - UIControl touch event tracking
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
// Fade in and update the popup view
CGPoint touchPoint = [touch locationInView:self];
// Check if the knob is touched. If so, show the popup view
if(CGRectContainsPoint(CGRectInset(self.thumbRect, -12.0, -12.0), touchPoint)) {
[self positionAndUpdatePopupView];
[self fadePopupViewInAndOut:YES];
}
return [super beginTrackingWithTouch:touch withEvent:event];
}
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
// Update the popup view as slider knob is being moved
[self positionAndUpdatePopupView];
return [super continueTrackingWithTouch:touch withEvent:event];
}
-(void)cancelTrackingWithEvent:(UIEvent *)event {
[super cancelTrackingWithEvent:event];
}
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
// Fade out the popup view
[self fadePopupViewInAndOut:NO];
[super endTrackingWithTouch:touch withEvent:event];
}
#pragma mark - Helper methods
-(void)constructSlider {
CGRect frame = CGRectMake(150, 230, 300.0, 10.0);
_popupView = [[ANPopoverView alloc] initWithFrame:frame];
_popupView.backgroundColor = [UIColor clearColor];
_popupView.alpha = 0.0;
[self addSubview:_popupView];
}
-(void)fadePopupViewInAndOut:(BOOL)aFadeIn {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
if (aFadeIn) {
_popupView.alpha = 1.0;
} else {
_popupView.alpha = 0.0;
}
[UIView commitAnimations];
}
-(void)positionAndUpdatePopupView {
CGRect zeThumbRect = self.thumbRect;
CGRect popupRect = CGRectOffset(zeThumbRect, 0, -floor(zeThumbRect.size.height * 1.5));
_popupView.frame = CGRectInset(popupRect, -20, -10);
_popupView.value = self.value;
}
#pragma mark - Property accessors
-(CGRect)thumbRect {
CGRect trackRect = [self trackRectForBounds:self.bounds];
CGRect thumbR = [self thumbRectForBounds:self.bounds trackRect:trackRect value:self.value];
return thumbR;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
and here is my popover class
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.font = [UIFont boldSystemFontOfSize:15.0f];
UIImageView *popoverView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"sliderlabel.png"]];
[self addSubview:popoverView];
textLabel = [[UILabel alloc] init];
textLabel.backgroundColor = [UIColor clearColor];
textLabel.font = self.font;
textLabel.textColor = [UIColor colorWithWhite:1.0f alpha:0.7];
textLabel.text = self.text;
textLabel.textAlignment = NSTextAlignmentCenter;
textLabel.frame = CGRectMake(0, -2.0f, popoverView.frame.size.width, popoverView.frame.size.height);
[self addSubview:textLabel];
}
return self;
}
-(void)setValue:(float)aValue {
_value = aValue;
self.text = [NSString stringWithFormat:#"%4.2f", _value];
textLabel.text = self.text;
[self setNeedsDisplay];
}
I tried the sample project and work with simple function
#property (strong, nonatomic) IBOutlet UISlider *slider; // make the getter and setter for Slider
#property (strong, nonatomic) IBOutlet UILabel *lblText; // make the getter and setter for label
- (void)viewDidLoad {
[super viewDidLoad];
// set verticical of UIslider
CGAffineTransform trans = CGAffineTransformMakeRotation(M_PI * 0.5);
self.slider.transform = trans;
// get the events of UISlider
[self.slider addTarget:self
action:#selector(sliderDidEndSliding:)
forControlEvents:(UIControlEventTouchUpInside | UIControlEventTouchUpOutside)];
}
// this method used for hide the label after changed the value
- (void)sliderDidEndSliding:(NSNotification *)notification {
NSLog(#"Slider did end sliding...");
[self.lblText removeFromSuperview];
}
// the slider value change method
- (IBAction)slider:(UISlider*)sender
{
// add the subview the lable to slider
[self.slider addSubview:self.lblText];
self.lblText.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"sliderlabel.png"]];
self.lblText.text = [NSString stringWithFormat:#"%4.2f",sender.value];
// change the label position depend upon slider move
self.lblText.center = CGPointMake(self.slider.value*self.slider.bounds.size.width,80);
[self.lblText setTransform:CGAffineTransformMakeRotation(-M_PI / 2)];
}
here I attached the sample project for UISlider the download link
I've got a very weird problem with my iOS project.
I have a UIViewController with some labels and buttons and when user enters this view I programmatically build a gameboard (it's the minesweeper game).
What I wanted to do now is add a simple element (in my case a segmented control but i tried also with a button and doesn't work) at the bottom or at the beginning of the board.
When I add something from the Interface Builder I can see that in the storyboard but when I run the app puff, it's disappeared!
View contains only the "old" elements and the new one is not shown.
Yes I made a IBOutlet:
#property (weak, nonatomic) IBOutlet UISegmentedControl *clickTypeSegmentedControl;
And yes I checked if references of segemented control are ok.
I can't really understand what is wrong, if someone can help I'll be very grateful.
** EDIT: I added code of the ViewController, there are some other methods I left out because they only handle the game. I repeat: even if I add a simple button with no actions, it will not be shown.
#import "GameViewController.h"
#import "GameBoardModel.h"
#import "ScoreViewController.h"
#import <AVFoundation/AVFoundation.h>
#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#interface GameViewController ()
#property (weak, nonatomic) IBOutlet UILabel *timerLabel;
#property (weak, nonatomic) IBOutlet UILabel *bombsLabel;
#property (weak, nonatomic) IBOutlet UIButton *restartButton;
#property(nonatomic, strong) AVAudioPlayer *soundEffects;
#property (weak, nonatomic) IBOutlet UISegmentedControl *clickTypeSegmentedControl;
#end
#implementation GameViewController
#synthesize difficulty, timer, playerName, scoreToAdd, clickTypeSegmentedControl;
double secondsPassed=-1.0f;
int seconds=0;
int totalRowsCols=0, totalMines=0, heightWidth=0, clicKNumber=0;
static NSString * const IMAGE_NAME_FLAG = #"flag.png";
static NSString * const IMAGE_NAME_BOMB_X = #"bomb.png";
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
UIBarButtonItem *backButton =[[UIBarButtonItem alloc]initWithTitle:#"MenĂ¹" style:UIBarButtonItemStyleBordered target:self action:#selector(popAlertAction:)];
self.navigationItem.leftBarButtonItem=backButton;
clickTypeSegmentedControl.selectedSegmentIndex = 0;
rootController = (BombsSeekerViewController *)[self.navigationController.viewControllers objectAtIndex:0];
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
playerName = [defaults objectForKey:#"NomeGiocatore"];
scoreToAdd = [[NSMutableDictionary alloc]init];
[self createGameBoard:difficulty];
[self newGame:nil];
}
-(void) newGame:(UIButton *)sender {
[self.view.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
clicKNumber=0;
secondsPassed=0;
self.timerLabel.text = [self labelString:secondsPassed];
self.game = [[Game alloc] initWithWidth:totalRowsCols AndHeight:totalRowsCols AndMineCount:totalMines];
self.buttonArray = [[NSMutableArray alloc] init];
[self.restartButton addTarget:self action:#selector(newGame:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.restartButton];
[self.view addSubview:self.timerLabel];
[self.view addSubview:self.bombsLabel];
for(int i = 0; i < totalRowsCols; i++) {
for (int j = 0; j < totalRowsCols; j++) {
self.button = [[Tile alloc] initWithLocation: CGPointMake(j,i) andHW:heightWidth andBlock:self.game.board[j][i] andTag:(i*totalRowsCols+j)];
[self.buttonArray addObject:self.button];
[self.view addSubview:self.button];
[self.button addTarget:self action:#selector(click:) forControlEvents:UIControlEventTouchUpInside];
}
}
[self.timer invalidate];
}
- (void) createGameBoard:(int)diff{
switch (diff) {
case 1:
totalRowsCols = 6;
totalMines = 2;
heightWidth = 44;
break;
case 2:
totalRowsCols = 8;
totalMines = 16;
heightWidth = 35;
break;
case 3:
totalRowsCols = 10;
totalMines = 25;
heightWidth = 29;
break;
}
self.flagCount = totalMines;
_bombsLabel.text = [NSString stringWithFormat:#"%d",self.flagCount];
}
-(NSString *) labelString: (int) num {
if(num < 10) {
return [NSString stringWithFormat:#"00%i",num];
}
else if(self.timeCount.intValue < 100) {
return [NSString stringWithFormat:#"0%i",num];
}
else if(self.timeCount.intValue < 1000) {
return [NSString stringWithFormat:#"%i",num];
}
else
return #"---";
}
-(void) click:(Tile *)sender {
if(clicKNumber <1){
[self refreshLabel:self.timer];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(refreshLabel:)
userInfo:nil
repeats:YES];
clicKNumber++;
}
if(self.game.gameStatus == STATUS_LOST || self.game.gameStatus == STATUS_WON) return;
if (self.clickTypeSegmentedControl.selectedSegmentIndex == 0 && sender.block.marking == MARKING_BLANK) {
[self.game clickedAtColumn: sender.point.x AndRow: sender.point.y];
for(Tile *b in self.buttonArray) {
if(b.block.marking == MARKING_CLICKED) {
b.enabled = NO;
}
else if(b.block.marking == MARKING_BLANK) {
[b setTitle:#"" forState:UIControlStateNormal];
[b setImage:nil forState:UIControlStateNormal];
}
}
}
else if (self.clickTypeSegmentedControl.selectedSegmentIndex == 1){
if(sender.block.marking == MARKING_BLANK && self.flagCount > 0) {
sender.block.marking = MARKING_FLAGGED;
self.flagCount--;
_bombsLabel.text = [NSString stringWithFormat:#"%d",self.flagCount];
[sender setImage:[UIImage imageNamed:IMAGE_NAME_FLAG] forState:UIControlStateNormal];
}
else if(sender.block.marking == MARKING_FLAGGED) {
sender.block.marking = MARKING_BLANK;
self.flagCount++;
_bombsLabel.text = [NSString stringWithFormat:#"%d",self.flagCount];
[sender setImage:[UIImage imageNamed:#"tile.png"] forState:UIControlStateNormal];
}
}
if(self.game.gameStatus == STATUS_LOST) [self gameLost:sender];
else if(self.game.gameStatus == STATUS_WON) [self gameWon];
}
If this is what I think it is, you should not be doing this:
[self.view.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
When the viewDidLoad is called, the newGame method is called, which removes al subviews from Superview. Including the ones added in Storyboard.
Make sure controller in storyboard is linked with the right controller class.
You call this method during -viewDidLoad:
-(void) newGame:(UIButton *)sender {
[self.view.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
//...
}
Thus, anything you put in your nib is getting removed after it loads.
I'm relativity new to Xcode and I was making a small just-for-fun kinda game for the iPhone and I'm having some trouble as far as making a certain sprite disappear when it spawns.
Brief:
So basically I have a sprite spawning on screen each time every 0.70 seconds and that sprite is set to a global IBOutlet variable called "Circle". These circles spawn at random locations around the screen of the iPhone at random widths and heights.
Overall
So basically I'm trying to make it so that when you click on the exact circle, it only hides that ball.
Thanks. Also, sorry to the mods for the sloppy formatting.
Here's the code in file that's relevant:
GameController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.gameState = GameStatePaused;
circleVerlocity = CGPointMake(CircleSpeedX, CircleSpeedY);
[NSTimer scheduledTimerWithTimeInterval:SpawnSpeed target:self selector:#selector(addCircle:) userInfo:nil repeats:YES];
[NSTimer scheduledTimerWithTimeInterval:BallSpeed target:self selector:#selector(gameLoop) userInfo:nil repeats:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)addCircle: (NSTimer *) aTimer {
if(gameState == GameStateRunning)
{
UIImageView *Circle1 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"sprite-small-1.png"]];
self.Circle = Circle1;
CGRect rect = CGRectMake(arc4random() % (274), arc4random() % (532), 50, 50);
[Circle1 setFrame:rect];
[self.view addSubview:Circle1];
}
}
-(void)gameLoop
{
if(gameState == GameStateRunning)
{
Circle.center = CGPointMake(Circle.center.x + circleVerlocity.x, Circle.center.y + circleVerlocity.y);
if(Circle.center.x > self.view.bounds.size.width || Circle.center.x < 0)
{
circleVerlocity.x = -circleVerlocity.x;
}
if(Circle.center.y > self.view.bounds.size.height || Circle.center.y < 0)
{
circleVerlocity.y = -circleVerlocity.y;
}
}else{
if(tapToBegin.hidden)
{
tapToBegin.hidden = NO;
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if(gameState == GameStatePaused)
{
tapToBegin.hidden = YES;
gameState = GameStateRunning;
}
if([touch view] == Circle)
{
Circle.hidden = YES;
}
}
You could use GestureRecognizer in your circle. with class UITapGestureRecognizer you could add a recognizer to an element,
circle.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideThatBall)];
[circle addGestureRecognizer:tapGesture];
-(void)hideThatBall{
circle.hidden = YES;
}
I would use something like this
I am trying to create a long press gesture that presents a second view controller, once the press has been held for 3 seconds. However, I only want the second view controller presented if the device is in a certain accelerometer orientation for the ENTIRE 3 seconds. That is, if the gesture is not held long enough or the device is tilted too much, the gesture is dismissed and the user must try again.
// In FirstViewController.h
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
#interface FirstViewController : UIViewController
#property (nonatomic, strong) CMMotionManager *motionManager;
#end
// In FirstViewController.m
#import "FirstViewController"
#import "SecondViewController"
#implementation motionManager;
- (void) viewDidLoad
{
[super viewDidLoad];
self.motionManager = [[CMMotionManager alloc]init];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPress:)];
longPress.minimumPressDuration = 3.0;
[self.view addGestureRecognizer:longPress];
}
- (void) handleLongPress: (UILongPressGestureRecognizer *)sender
{
// Not sure what to do here
}
I have previously tried chunks of code in the last method but it looks obnoxious and just is not correct. Instead, I have listed several lines of code below that I know work individually, but I need assistance in making them all work together.
// Accelerometer
if ([self.motionManager isAccelerometerAvailable])
{
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData,NSError *error)
{
if (ABS(accelerometerData.acceleration.x) < 0.3 && ABS(accelerometerData.acceleration.y) < 0.30 && ABS(accelerometerData.acceleration.z) > 0.70) // Phone is flat and screen faces up
{
NSLog(#"Correct Orientation!!!");
[self.motionManager stopAccelerometerUpdates];
}
else
{
NSLog(#"Incorrect orientation!!!");
[self.motionManager stopAccelerometerUpdates];
}];
}
else
{
NSLog(#"Accelerometer is not available.");
}
// Go to second view controller
if (sender.state == UIGestureRecognizerStateBegan)
{
SecondViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self presentViewController:svc animated:YES completion:nil];
}
Any ideas? Or even a more general way to cancel the gesture unless a condition is met would be very helpful.
I hope the code is verbose enough for you to read, it's pretty self explanatory - I've stuck with your accelerometer code, and used the same variable names.
#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
#interface ViewController () {
NSOperationQueue *motionQueue;
NSTimer *touchTimer;
NSTimeInterval initialTimeStamp;
BOOL touchValid;
float timerPollInSeconds;
float longPressTimeRequired;
}
#property (strong, nonatomic) NSTimer *touchTimer;
#property (assign, nonatomic) NSTimeInterval initialTimeStamp;
#property (assign, nonatomic) BOOL touchValid;
#property (assign, nonatomic) float timerPollInSeconds;
#property (assign, nonatomic) float longPressTimeRequired;
#property (strong, nonatomic) CMMotionManager *motionManager;
#property (strong, nonatomic) NSOperationQueue *motionQueue;
#end
#implementation ViewController
#synthesize touchTimer = _touchTimer, initialTimeStamp, touchValid, motionQueue = _motionQueue;
#synthesize timerPollInSeconds, longPressTimeRequired, motionManager = _motionManager;
- (void)viewDidLoad
{
self.timerPollInSeconds = 0.25f;
self.longPressTimeRequired = 3.0f;
self.touchTimer = nil;
self.touchValid = NO;
self.initialTimeStamp = NSTimeIntervalSince1970;
self.motionManager = [[CMMotionManager alloc] init];
self.motionQueue = [[NSOperationQueue alloc] init];
[_motionQueue setName:#"MotionQueue"];
[_motionQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
[super viewDidLoad];
self.view.multipleTouchEnabled = NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Operations
-(void) startLongPressMonitorWithTimeStamp:(NSTimeInterval) timeStamp {
NSLog(#"Starting monitoring - %g", timeStamp);
if( self.touchTimer ) {
if( [_touchTimer isValid] ) {
[_touchTimer invalidate];
}
}
self.touchTimer = [NSTimer timerWithTimeInterval:self.timerPollInSeconds target:self selector:#selector(timerPolled:) userInfo:nil repeats:YES];
if( [_motionManager isAccelerometerAvailable] ) {
NSLog(#"Accelerometer Available");
if( ![_motionManager isAccelerometerActive] ) {
NSLog(#"Starting Accelerometer");
[_motionManager startAccelerometerUpdatesToQueue:self.motionQueue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
if (ABS(accelerometerData.acceleration.x) < 0.3 && ABS(accelerometerData.acceleration.y) < 0.30 && ABS(accelerometerData.acceleration.z) > 0.70) // Phone is flat and screen faces up
{
dispatch_sync(dispatch_get_main_queue(), ^{
self.touchValid = YES;
});
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
self.touchValid = NO;
[self stopLongPressMonitoring:YES];
});
};
}];
}
else {
NSLog(#"Accelerometer already active");
}
}
else {
NSLog(#"Accelerometer not available");
}
self.initialTimeStamp = timeStamp;
self.touchValid = YES;
[_touchTimer fire];
[[NSRunLoop mainRunLoop] addTimer:self.touchTimer forMode:NSRunLoopCommonModes];
}
-(void) stopLongPressMonitoring:(BOOL) touchSuccessful {
[_motionManager stopAccelerometerUpdates];
[_touchTimer invalidate];
self.touchValid = NO;
if( touchSuccessful ) {
NSLog(#"Yes");
}
else {
NSLog(#"No");
}
}
#pragma mark - User Interaction
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//We're using the current times, interval since the touches timestamp refers to system boot up
// it is more than feasible to use this boot up time, but for simplicity, I'm just using this
NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
[self startLongPressMonitorWithTimeStamp:timestamp];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if( self.touchValid && [NSDate timeIntervalSinceReferenceDate] - self.initialTimeStamp == self.longPressTimeRequired ) {
[self stopLongPressMonitoring:YES];
}
else {
[self stopLongPressMonitoring:NO];
}
}
#pragma mark - Timer Call back
-(void) timerPolled:(NSTimer *) timer {
NSTimeInterval firedTimeStamp = [NSDate timeIntervalSinceReferenceDate];
NSLog(#"Timer polled - %g", firedTimeStamp);
if( self.touchValid ) {
NSLog(#"Time elapsed: %d", (int)(firedTimeStamp - self.initialTimeStamp));
if( firedTimeStamp - self.initialTimeStamp >= self.longPressTimeRequired ) {
NSLog(#"Required time has elapsed");
[self stopLongPressMonitoring:YES];
}
}
else {
NSLog(#"Touch invalidated");
[self stopLongPressMonitoring:NO];
}
}
#end
You may be able to do what you want by subclassing UIGestureRecognizer to make your own gesture recognizer similar to UILongPressGestureRecognizer that listens for accelerometer data in addition to presses of at least 3 second duration.
I'd probably override the onTouchesBegan, and onTouchesEnded methods, rather than using a gesture recogniser.
I'd then create a NSTimer object, an NSTimeInterval var and a BOOL in your view controller; for the sake of it, I'll call them, touchTimer, initialTouchTimeStamp, and touchValid.
for the sake of complexity, it's an assumption that the viewControllers view is not multi-touch.
Assume the repeatTime = 0.25f;
longPressTimeRequired = 3;
The timer selector would incorporate your accelerometer method, if the data inside your accelerometer method is invalid, I'd set a touchValid to false (and invalidate the timer), otherwise I'd set it to true After checking the accelerometer, I'd check if my initialTouchTimeStamp var is longPressTimeRequired or more seconds prior to [touchTimer fireDate] - repeatTime , if it is, and the touchValid is true, then I would go to my second controller.
onTouchesBegan, I'd invalidate touchTimer and create a new one that would repeat every repearTime seconds, and lasts for y seconds. Set touchValid to NO, and set initialTouchTimeStamp to touch.timestamp.
onTouchesEnded, I'd invalidate touchTimer, and check if my initialTouchTimeStamp var is longPressTimeRequired or more seconds prior to [touchTimer fireDate] - repeatTime , if it is, and the touchValid is true, then I would go to my second controller.
there are many common elements in here, and it's probably not the most elegant way of doing things, but it should work. Hope this helps.
I started to learn Objective-C and use cocos2D about 1 month ago.
I want to replace three different layers by tapping buttons.
At first, I tried to use "CCLayerMultiplex." Then, use "if sentence." But then the layers just overlap or crash when the buttons are tapped. I want the previous layer to disappear when the new layer appears, but the old layer remains with my code now.
I think using "CCLayerMultiplex" is my best option, but I can't make it work as I want it to.
Below is my code. I'm afraid that there're some poor sentences...
#interface GSLayout : CCLayer {
// button items
CCMenuItemImage *file1;
CCMenuItemImage *file1Pushed;
...
// Layer (replace)
CCLayer *layer1;
CCLayer *layer2;
CCLayer *layer3;
// replace layers
CCLayerMultiplex* mpLayer;
}
#end
#implementation GSLayout
-(id) init{
if( (self=[super init])) {
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
self.isTouchEnabled = YES;
// buttons
file1 = [CCMenuItemImage itemFromNormalImage:#"Icon-Small-50.png"
selectedImage: #"Icon-Small.png"
target:nil
selector:nil];
file1Pushed = [CCMenuItemImage itemFromNormalImage:#"Icon-Small.png"
selectedImage:#"Icon-Small-50.png"
target:nil
selector:nil];
CCMenuItemToggle *toggleFile1 = [CCMenuItemToggle itemWithTarget:self
selector:#selector(selectOne:)
items:file1,file1Pushed, nil];
toggleFile1.anchorPoint = CGPointMake(0.5f, 0.5f);
file2 = [[CCMenuItemImage itemFromNormalImage:#"Icon-Small-50.png"
selectedImage: #"Icon-Small.png"
target:nil
selector:nil]retain];
file2Pushed = [[CCMenuItemImage itemFromNormalImage:#"Icon-Small.png"
selectedImage:#"Icon-Small-50.png"
target:nil
selector:nil]retain];
CCMenuItemToggle *toggleFile2 = [CCMenuItemToggle itemWithTarget:self
selector:#selector(selectTwo:)
items:file2,file2Pushed, nil];
toggleFile2.anchorPoint = CGPointMake(0.5f, 0.5f);
...
CCMenu *toggleMenu = [CCMenu menuWithItems:toggleFile1,toggleFile2,toggleFile3, nil];
[toggleMenu alignItemsHorizontally];
toggleMenu.anchorPoint = CGPointMake(0, 1.0f);
CGSize screenSize = [[CCDirector sharedDirector] winSize];
toggleMenu.position = CGPointMake(screenSize.width/2, screenSize.height);
[self addChild:toggleMenu];
// create layers
layer1 = [GameFile1 node];
layer2 = [GameFile2 node];
layer3 = [GameFile3 node];
mpLayer = [CCLayerMultiplex layerWithLayers:layer1,layer2,layer3, nil];
}
return self;
}
- (void) selectOne: (CCMenuItem*) menuItem
{
NSLog(#"The first menu was called");
if([layer1 isRunning])
{
nil;
NSLog(#"The layer1 is running");
} else if([layer2 isRunning]) {
[mpLayer switchTo:0];
NSLog(#"The layer2 was replaced");
}else if([layer3 isRunning]) {
[mpLayer switchTo:0];
NSLog(#"The layer3 was replaced");
} else{
[self addChild:layer1];
NSLog(#"The layer1 was called");
}
}
- (void) selectTwo: (CCMenuItem*) menuItem
{
NSLog(#"The second menu was called");
if([layer2 isRunning])
{
nil;
NSLog(#"The layer2 is running");
} else if([layer1 isRunning]) {
[mpLayer switchTo:1];
NSLog(#"The layer1 was replaced");
} else if([layer3 isRunning]) {
[mpLayer switchTo:1];
NSLog(#"The layer3 was replaced");
}else{
[self addChild:layer2];
NSLog(#"The layer2 was called");
}
}
- (void) selectThree: (CCMenuItem*) menuItem
{
NSLog(#"The third menu was called");
...
}
Please give me some advice!
Thank you in advance!
I'll add some codes.
#interface GameFile1 : CCLayer {
CCSprite* sprite;
}
#implementation GameFile1
-(id) init{
if( (self=[super init])) {
CGSize screenSize = [[CCDirector sharedDirector] winSize];
CCMenuItemImage *soundItem1 = [CCMenuItemImage itemFromNormalImage:#"button1.png"
selectedImage: #"Icon.png"
target:self
selector:#selector(doSomethingOne:)];
CCMenuItemImage *soundItem2 = [CCMenuItemImage itemFromNormalImage:#"button2.png"
selectedImage: #"Icon.png"
target:self
selector:#selector(doSomethingTwo:)];
...
CCMenu * myMenu = [CCMenu menuWithItems:soundItem1, soundItem2,soundItem3,soundItem4, soundItem5,soundItem6,soundItem7, soundItem8,soundItem9,soundItem10,soundItem11,soundItem12, nil];
[myMenu alignItemsInRows:[NSNumber numberWithInt:4],[NSNumber numberWithInt:4],[NSNumber numberWithInt:4] ,nil];
myMenu.position = CGPointMake(370, 120);
[self addChild:myMenu];
sprite = [CCSprite spriteWithFile:#"o0400026611355530621.jpg"];
sprite.scale = 0.5f;
sprite.position = CGPointMake(screenSize.width/2, screenSize.height/2);
sprite.anchorPoint = CGPointMake(1.0f, 0);
[self addChild:sprite];
- (void) doSomethingOne: (CCMenuItem *) menuItem
{
NSLog(#"The first menu was called");
[[SimpleAudioEngine sharedEngine] playEffect:#"one.wav"];
[sprite setTexture:[[CCTextureCache sharedTextureCache] addImage: #"one.jpg"]];
}
- (void) doSomethingTwo: (CCMenuItem *) menuItem
{
NSLog(#"The second menu was called");
[[SimpleAudioEngine sharedEngine] playEffect:#"two.wav"];
[sprite setTexture:[[CCTextureCache sharedTextureCache] addImage: #"one.jpg"]];
}
...
#end
Since you have already loaded the textures for all three layers, you might as well have all three sitting in separate CCLayers where two are hidden at any given state using a method similar to:
- (void) setLayer:(uint)show {
layerOne.visible = (show == 0);
layerTwo.visible = (show == 1);
layerThree.visible = (show == 2);
}