I'm trying to pass an object from an SKScene to the current UIViewController where the scene is being shown, it's like I created a label that will only be triggered once the object from the scene reached a specific location, I understand that I can just easily create a SKLabel and have it added to the scene once the object reaches the location I want it to, but I'd rather do it the ViewController style way since I'll be adding a lot of objects that will do the same thing as my app Progress, that reason step aside, I did actually tried adding an sk label to see if it will work that way, Yes I was able to see the SKLabel appear upon the object reaching let's say location.x = 50 and I set the node to be removed when the object reaches location.x = 270, But the problem is it's only doing it once, after the object being added again, it seems that the scene is not removing the node even though I'm pointing my object to hit 270..
By the way, since I mentioned 2 problems, here's the code that executes the said operation for the SKlabel node which is only happening once, I want it to execute the statement one time, everytime I hit that location
if (newLocation.x==270.00 )) {
[self addingTheLabel];
}
if (newLocation.x == 50.00) {
SKAction *removingTheNode = [SKAction removeFromParent];
[self.label runAction:removingTheNode];
}
Nevermind, was able to resolve the issue now..
For those people who might encounter this,Creating a protocol on your scene will fix the issue:
#protocol gameSceneDelegate <NSObject>
-(void)testProtocol;
-(void)testProtocol2;
#end;
#interface MyScene : SKScene
#property (weak,nonatomic) id<gameSceneDelegate> delegate;
Implement it on your scene's view controller:
#interface ViewController : UIViewController<gameSceneDelegate>
in ViewController.m you need to set first your scene as your delegate:
MyScene *scenePointer = (MyScene*) scene;
[scenePointer setDelegate:self];
and finally, implement the methods on your ViewController:
-(void)testProtocol{
NSString *sampleString2 = [[NSString alloc]initWithFormat:#"This will show when testProtocol is selected"];
self.sampleLabel.text = sampleString2;
}
-(void)testProtocol2{
NSString *sampleString3 = [[NSString alloc]initWithFormat:#"This will show when test 2 protocol is selected"];
self.sampleLabel.text = sampleString2;
}
Make an if statement inside your ViewDidLoad that if your scenePoint is the delegate do the following:
if([scenePointer delegate]){
[self testProtocol];
[self testProtocol2];
}
Now, Going to your Scene, since what I want is for the label to change whenever the SpriteNode hits a specific location, what I did is:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *touch in touches){
CGPoint location = [touch locationInNode:self];
CGPoint newLocation = CGPointMake(location.x, 450);
if (newLocation.x == 270) {
//This will trigger the method everytime the spritenode hit's this location
if([_delegate respondsToSelector:#selector(testProtocol2)]){
[_delegate performSelector:#selector(testProtocol2)];
}
}
if(newLocation.x <= 220){
newLocation.x = 220;
//This will trigger the method everytime the spritenode hit's this location
if([_delegate respondsToSelector:#selector(testProtocol)]){
[_delegate performSelector:#selector(testProtocol)];
}
}
self.spriteNode.position = newLocation;
}
}
It will now change the label every time your SpriteNode hits the location you want it to.
I hope this will help other's who are in the same concept as this.
import your appdelegate and then use
ViewController *vc = (ViewController*)[AppDelegate*)[[UIApplication sharedApplication] delegate] viewController];
to parse the object to your presented UIViewController
Related
So I create a delegate protocol and corresponding property in my scene. In my view controller that holds all my scenes I set the scenes delegate to itself so I can call a fullscreen ad(from the view controller) in the menu which is the first game scene called. This works great I can call the method. But after i transition to the level scenes then back to the menu scene calling my method to show fullscreen ad does nothing. Im assuming because the delegate being set to itself has been lost. How can I set the delegate back to itself within the game scene? This is how I do it inside the View controller.M
myScene = [GameScene unarchiveFromFile:#"GameScene"];
((GameScene *)myScene).mySceneDelegate = self; // i need to be able to do this within the scene
myScene.scaleMode = SKSceneScaleModeResizeFill;
// Present the scene.
[skView presentScene:myScene];
How I call the fullScreen method thats in my VC from my menu scene...
[self.mySceneDelegate showFullScreen:self];
...so in my code when I transition from my LevelOne scene I try to reassign the delegate with the following code but it nothing happens
-(void)goBackToMenu{
GameScene *newscene = [GameScene sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsCloseHorizontalWithDuration:1]];
((GameScene *)newscene).mySceneDelegate = self;
}
I have also tried setting the property attribute to Strong instead of weak with no luck either
**********************ALL CODE **************************************
More code.. my Viewcontroller.h
#protocol TCAMySceneDelegate;
#interface GameViewController : UIViewController<TCAMySceneDelegate>
#property(nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;
#end
my view controller.m
- (void)viewDidLoad
{
[super viewDidLoad];
SKView * skView = (SKView *)self.view;
myScene = [GameScene unarchiveFromFile:#"GameScene"];
((GameScene *)myScene).mySceneDelegate = self;
// Present the scene.
[skView presentScene:myScene];
}
-(void)showFullScreen:(GameScene *)gameScene{
NSLog(#"Show Full Screen");
[revmobFS loadWithSuccessHandler:^(RevMobFullscreen *fs) {
[fs showAd];
NSLog(#"Ad loaded");
} andLoadFailHandler:^(RevMobFullscreen *fs, NSError *error) {
NSLog(#"Ad error: %#",error);
} onClickHandler:^{
NSLog(#"Ad clicked");
} onCloseHandler:^{
NSLog(#"Ad closed");
}];
[RevMobAds startSessionWithAppID:#"547251235f2a043608a66f1a"
withSuccessHandler:^{
NSLog(#"Session started with block");
// Here, you should call the desired ad unit
revmobFS = [[RevMobAds session] fullscreen];
[revmobFS showAd];
// [RevMobAds session].testingMode = RevMobAdsTestingModeWithAds;
revmobFS.delegate = self;
[revmobFS loadAd];
} andFailHandler:^(NSError *error) {
NSLog(#"Session failed to start with block");
}];
}
my game scene.h file..
#protocol TCAMySceneDelegate;
#interface GameScene : SKScene
#property (nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;
#end
#protocol TCAMySceneDelegate <NSObject>
-(void)showFullScreen:(GameScene *)gameScene;
#end
my game scene.m file..
-(void)didMoveToView:(SKView *)view {
//Show Ad
[self.mySceneDelegate showFullScreen:self];
}
now the above showFullScreen: is called but online when the gameScene (my game menu) is presented the first time .. after transitioning to other scenes in the games such as my levels then go back to my menu(gamescene) it never gets called. Hope this is more clear now
how i switch to another scene from my game scene.m
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
NSLog(#"Level Selected: %#", node.name);
int x = [node.name intValue];
switch (x) {
case 1:{
LevelOne *newscene = [LevelOne sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
break;
}
case 2:{
LevelTwo *newscene = [LevelTwo sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
case 3:{
LevelThree *newscene = [LevelThree sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
//..........
default:
break;
}
}
LevelOne.h code...
#import <SpriteKit/SpriteKit.h>
#import "DrawCanvas.h"
#import "GameScene.h"
#interface LevelOne : SKScene<SKPhysicsContactDelegate>
#end
Personally, I found your description as well as the code you posted a bit lacking in details as well as being a bit confusing. When providing code you will probably get better/quicker help if you provide more detail. For example, which file does goBackToMenu belong to?
How/in what context is
[self.mySceneDelegate showFullScreen:self];
called? I would assume that it is from some trigger point in the GameScene. And what is the method signature of showFullScreen?
And since you don't indicate what type mySceneDelegate is, you're making the readers guess what you mean.
So trying to decipher what you are asking...
Let's assume you have some delegate which is called GameSceneDelegate. I am also going to assume you already know SKSceneDelegate exists and are either using it already but just haven't posted that code or don't need to use it.
Your GameViewController would have it's interface defined as:
#interface GameViewController : UIViewController<GameSceneDelegate>
And your GameScene would have a property defined like this:
#property (nonatomic, weak) id<GameSceneDelegate> mySceneDelegate;
Note you should be using weak here. Because if your GameViewController ever has a strong reference to your GameScene, then you've just created a retain cycle.
You have a few options of when to set mySceneDelegate. If I know I must have a delegate defined, what I often do is add it as an argument to the initializers. This way it is clear that it must have a delegate. So, given you are using class methods to generate instances, you'd do something like this:
#interface GameScene : SKScene
#property (nonatomic, weak) id<GameSceneDelegate> mySceneDelegate;
+ (instancetype)unarchiveFromFile:(NSString *)file mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate;
+ (instancetype)sceneWithSize:(CGSize)size mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate;
#end
And you those methods would look like:
+ (instancetype)unarchiveFromFile:(NSString *)file mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
GameScene *scene = [super unarchiveFromFile:file];
scene.mySceneDelegate = mySceneDelegate;
return scene;
}
+ (instancetype)sceneWithSize:(CGSize)size mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
GameScene *scene = [super sceneWithSize:size];
scene.mySceneDelegate = mySceneDelegate;
return scene;
}
Note to make this work, you'll need to define this in the GameViewController.h (or a separate file for the category if you wish)
#interface SKScene (Unarchive)
+ (instancetype)unarchiveFromFile:(NSString *)file;
#end
The alternative is to immediately set mySceneDelegate right after creating your instance of the scene.
Realistically, if you are doing this, it should work and your delegate's should stay in tact. FWIW, I am actually doing this in my game. So I know it works.
Okay, so this is where things are hazy in your question. You indicate the delegate is "lost". The only way the delegate can be "lost" would be if someone explicitly sets it to nil. Or the other possiblity I suppose is your GameScene is being dealloc'd and you don't realize it.
I'd setup two things to help debug it.
In GameScene add
- (void)dealloc
{
NSLog(#"I'm being dealloc'd");
}
- (void)setMySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
_mySceneDelegate = mySceneDelegate;
NSLog(#"Setting mySceneDelegate");
}
So now you can try and determine where it is "getting lost". If any of those print out to the console at the time it is lost, simply set a breakpoint in those methods to see who is calling it.
One other pieces of info that would also help are
What do you mean assigning a delegate to itself? I have no idea what
that means.
You mentioned you assume the delegate is lost. Why? Do you have
evidence or is this purely a guess?
If you can provide some answers and better detail, we can probably figure out what is going awry.
Okay, now looking at your code, I think your problem is here in GameScene.m:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
NSLog(#"Level Selected: %#", node.name);
int x = [node.name intValue];
switch (x) {
case 1:{
LevelOne *newscene = [LevelOne sceneWithSize:self.size];
newScene.mySceneDelegate = self.mySceneDelegate;
// Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
break;
}
case 2:{
LevelTwo *newscene = [LevelTwo sceneWithSize:self.size];
newScene.mySceneDelegate = self.mySceneDelegate;
// Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
case 3:{
LevelThree *newscene = [LevelThree sceneWithSize:self.size];
newScene.mySceneDelegate = self.mySceneDelegate;
// Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
//..........
default:
break;
}
}
What I am doing here is assigning the the new scene's mySceneDelegate to the existing one being used.
You can clean this code up to be more readable.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
NSLog(#"Level Selected: %#", node.name);
int x = [node.name intValue];
GameScene *newScene = nil;
switch (x) {
case 1:
newScene = [LevelOne sceneWithSize:self.size];
// Or, if you can make it work, newScene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
break;
case 2:
newScene = [LevelTwo sceneWithSize:self.size];
// Or, if you can make it work, newScene = [LevelTwo sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
break;
case 3:
newScene = [LevelThree sceneWithSize:self.size];
// Or, if you can make it work, newScene = [LevelThree sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
break;
// other cases..........
default:
break;
}
if (newScene) {
newScene.mySceneDelegate = self.mySceneDelegate;
[self.view presentScene:newScene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
}
For your levels, you would need to do the following:
#import <SpriteKit/SpriteKit.h>
#import "DrawCanvas.h"
#import "GameScene.h"
#interface LevelXYZ : GameScene<SKPhysicsContactDelegate>
#end
Where XYZ is the level (eg. One). By doing this, you inherit mySceneDelegate from GameScene. If for some reason you are insistent that the level scenes are not inheriting from GameScene, then add a base class which holds the delegate. So that would be something like:
#interface MyBaseScene : SKScene
#property(nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;
#end
And then GameScene would be this:
#interface GameScene : MyBaseScene
And your level scenes would be
#interface LevelXYZ : MyBaseScene<SKPhysicsContactDelegate>
Note a couple of things I did.
Changed newscene to newScene. In iOS, you typically use came case.
you were doing it for mySceneDelegate. Consistency is key.
Originally declared newScene as nil and assigned as need. Then at the end of the switch, if a newScene exists, assign the delegate and
present. This way, you are only doing it in one place. It makes life
easier. For example, if you wanted to change the duration for each
one, then what? You have to change ever case. Note there is a
downfall to this. If you wanted on level to have a special duration,
then it doesn't quite work. But you can change it by making the
duration and transition type variables.
BTW, I would strongly advise against having your scene class contain the code that does the actual transition. I would either have it call a delegate to do it or find another way (like using a notification that a scene change is needed). For example, in my current game, I have my view controller be the controller of all scene changes. The advantage of this is you can also call scene transitions from other external forces (and not have your scene be the one that manages it).
Lastly, you can hopefully see that the only way the issue was discoverable was by presenting the right code. I will agree it is hard to know what code to present, because if there is too much code, then people may not read it. It's a fine line, but some food for thought.
I am wanting to know when a user has touched anywhere on the screen of my app.
I have looked into using -(UIResponder *)nextResponder but unfortunately this will not work, as I am also reloaded a table automatically, so this gets trigged when that occurs.
I have also tried a gesture recognizer, with the following code. But this will only recognise touches on the view. Where as I have many buttons the user will be using to operate the app. I would like to avoid adding a gesture recogniser or code for this in every button and segment control I have on the screen
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnView:)];
[self.mainView addGestureRecognizer:tap];
- (void)tapOnView:(UITapGestureRecognizer *)sender
{
//do something
}
I have also tried -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event , but this has the same issue as the gesture recognizer.
I was wondering if there is any way I could achieve this task. I was hoping that I may be able to recognise the type of event from within the nextResponder, and then I could detect if it is button for example.
EDIT: The reason I am working on this is that my app needs to stay active and the screen cannot be locked (so I have disabled screen locking). To avoid excessive use of power, I need to dim the screen, but then return the brightness back to the original level once the app is touched. I need this feature to only occur on 1 of my viewcontrollers.
As mentioned by Ian MacDonald, using hitTest:: is a great solution to detect user interaction on an app wide scale, including when buttons, textfields, etc, are selected.
My solution was to subclass UIWindow and implement the hitTest method.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// do your stuff here
// return nil if you want to prevent interaction with UI elements
return [super hitTest:point withEvent:event];
}
You could attach your UITapGestureRecognizer to your [[UIApplication sharedApplication] keyWindow].
Alternatively, you could override hitTest: of your root UIView.
Is there a particular task you are hoping to accomplish? There may be a better way than assigning an "anywhere" gesture.
Edit: Use hitTest:.
#interface PassthroughView : UIView
#property (readonly) id target;
#property (readonly) SEL selector;
#end
#implementation PassthroughView
- (void)setTarget:(id)target selector:(SEL)selector {
_target = target;
_selector = selector;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
[_target performSelector:_selector];
return nil;
}
#end
#implementation YourUIViewController {
PassthroughView *anytouchView;
}
- (void)viewDidLoad {
// Add this at the end so it's above all other views.
anytouchView = [[PassthroughView alloc] initWithFrame:self.view.bounds];
[anytouchView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[anytouchView setTarget:self selector:#selector(undim)];
[anytouchView setHidden:YES];
[self.view addSubview:anytouchView];
}
- (void)undim {
[anytouchView setHidden:YES];
}
- (void)dim {
[anytouchView setHidden:NO];
}
#end
Your edit adds more clarity to your question.
The reason I am working on this is that my app needs to stay active
and the screen cannot be locked (so I have disabled screen locking).
To avoid excessive use of power, I need to dim the screen, but then
return the brightness back to the original level once the app is
touched.
Since you are controlling the screen brightness, you can add one transparent view controller before dimming screen on top of your root controller which does only one job, listen to tap using Tap gesture. And on tap you can dismiss the view controller and adjust brightness to previous state.
By doing so you dont have to worry about buttons being clicked as they will be below the transparent view controller. Since its a whole new view controller sitting on top of stack you dont have to modify your existing code as well.
Ok I have had a similar problem before.
As I remember I subclassed the UIWindow for full screen detection and made it First responder.
Than I overridden the touch to handle from subclasses.
You can also use code to identify the control that is been touched.
#import <QuartzCore/QuartzCore.h>
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setMultipleTouchEnabled:YES];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// Enumerate over all the touches
[touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
// Get a single touch and it's location
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.view];
...
}];
}
To disable the locking of screen I used below code:
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
I used following functions to dim or increase the screen brightness
[[UIScreen mainScreen] setBrightness:0.0f]; //and
[[UIScreen mainScreen] setBrightness:1.0f];
I want to hide/unhide grey colored view(Which has button) and it is on top of cream/biege colored webView - please see attached pic
I have used an page based application template available in xcode.
Approach 1:Hide/unhide inside controller
i have tried to hide/unhide in same controller but the problem is every time new instance of this controller created and bool values for hide/unhide are lost
Approach 2:Protocol & Delegates
I have also tried to use delegate/protocol to maintain its status from its parent controller but it never gets inside if block -
if ([self.delegateReadingToolbar........ block is never called.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if(touch)
{
CGPoint location = [touch locationInView: [touch view]];
if (CGRectContainsPoint(webViewTouch, location))
{
//do whatever
NSLog(#"webView Touched");
if (self.showReadingToolBar)
{
self.showReadingToolBar = NO; // approach 1
self.viewReadingToolBar.hidden = NO;
// approach 2
if ([self.delegateReadingToolbar respondsToSelector:#selector(contentViewDidFinish:showStatus:)])
{ // this block is never called
[self.delegateReadingToolbar contentViewDidFinish:self showStatus:NO];
}
}
else
{
self.showReadingToolBar = YES;
self.viewReadingToolBar.hidden = YES;
[self.delegateReadingToolbar contentViewDidFinish:self showStatus:YES];
}
}
}
}
For Approach 2 Coding:
ChildController.h
#class ChildController;
#protocol ReadingToolbarShowDelegate <NSObject>
-(void)contentViewDidFinish:(contentView *)controller showStatus:(BOOL)show;
#end
#property (nonatomic,weak)id<ReadingToolbarShowDelegate>delegateReadingToolbar;
ParentController.h
#import "ChildController.h"
#interface ParentController : UIViewController<UIPageViewControllerDataSource,UIPageViewControllerDelegate,ReadingToolbarShowDelegate>
ParentController.m
-(void)ChildControllerDidFinish:(contentView *)controller showStatus:(BOOL)show
{
showReadingToolbar = show;
}
If you alloc/init a new controller the previously set values will be gone. You are essentially creating a new object. I see a couple of different ways you may be able to do this.
Place your controllers inside a navigation controller and simply push the others on or off the stack. This way you don't have to instantiate a new object.
You could save the desired display configuration In an NSUserDefaults key/value pair. Whenever you need to create a new object of this type it looks up the value in NSUserDefault.
I have the regular OpenGL / EAGL setup going on:
#interface EAGLView : UIView {
#public
EAGLContext* context;
}
#property (nonatomic, retain) EAGLContext* context;
#end
#implementation EAGLView
#synthesize context;
+ (Class)layerClass {
return [CAEAGLLayer class];
}
#end
#interface EAGLViewController : UIViewController {
#public
EAGLView* glView;
}
#property(nonatomic, retain) EAGLView* glView;
#end
#implementation EAGLViewController
#synthesize glView;
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch* touch in touches) {
CGPoint location = [touch locationInView:glView];
int index;
for (index = 0; index < gCONST_CURSOR_COUNT; ++index) {
if (sCursor[index] == NULL) {
sCursor[index] = touch;
break;
}
}
}
[super touchesBegan:touches withEvent:event];
}
That implementation includes corresponding touchesEnded/Canceled/Moved as well. The code fully works and tracks well.
I also make sure that I'm giving proper values for everything:
sViewController = [EAGLViewController alloc];
CGRect rect = [[UIScreen mainScreen] applicationFrame];
sViewController.glView = [[EAGLView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)];
Assert(sViewController.glView);
sViewController.glView.userInteractionEnabled = YES;
sViewController.glView.multipleTouchEnabled = YES;
sViewController.glView.exclusiveTouch = YES;
It all compiles just fine, but I'm never receiving more than one UITouch. I don't mean in a single touchesBegan, but the index never goes past 0. I also set a breakpoint for the second time it enters that function, and putting two fingers on doesn't make it trigger.
If you want to detect multiple touches (and/or distinguish between a one finger, two finger etc. touch), try using a UIPanGestureRecognizer. When you set it up, you can specify the minimum and maximum number of touches. Then attach it to the view where you want to detect the touches. When you receive events from it, you can ask it how many touches it received and branch accordingly.
Here's the apple documentation:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPanGestureRecognizer_Class/Reference/Reference.html
If you do this, you might not need to use the touchesBegan/Moved/Ended methods at all and, depending on how you set up the gesturerecognizer, touchesBegan/Moved/Ended may never get called.
Use [event allTouches] in place of touches. touches represents only the touches that have 'changed'. From the apple docs:
If you are interested in touches that have not changed since the last
phase or that are in a different phase than the touches in the
passed-in set, you can find those in the event object. Figure 3-2
depicts an event object that contains touch objects. To get all of
these touch objects, call the allTouches method on the event object.
It seems all I was missing was this:
sViewController.view = sViewController.glView;
I am a newbie in Objective-C and trying to make a simple app in which when you touch the object it will move randomly for a while and stops. then you need to touch it again so it will move again and stop after a while.
I have searched for touch method and some tutorials, but the problem is I don't know how to start. It is like I need a function to move the object and one to touch it, but I don't know how to connect them and use them.
here is a tutorial which helped me a lot to get a view of functionality and it actually function in opposite way of my app. but still I can not start programming on my own.
http://xcodenoobies.blogspot.se/2010/11/under-construction.html
Any help would be appreciated, regarding how to program my logic and how to find the right methods and how to find the type of variables I need.
Step 1: Put this code in your ViewDidLoad Method, in which i have created some UIImageView and add it to View Randomly
[self.view setTag:1];
for(int i=0;i<4;i++)
{
int x = arc4random()%300;
int y = arc4random()%400;
#warning set Image here
UIImageView *imgview = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"someImage.png"]];
[imgview setFrame:CGRectMake(x, y, 25, 25)];
[imgview setUserInteractionEnabled:YES];
[self.view addSubview:imgview];
}
Step 2 : Define touchBegan Method to handle touch and move objects around the view, we have set Tag = 1 for ViewController ,because we dont want to move our mainview, only subviews will be moved
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if([[touch view] tag] != 1)
{
[UIView animateWithDuration:0.25f animations:^{
int x = arc4random()%300;
int y = arc4random()%400;
[[touch view] setCenter:CGPointMake(x, y)];
}];
}
}
What you need is to add a gesture recognizer to the view you want to be able to touch:
// In a view controller or in a UIView subclass
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[self addGestureRecognizer:tapGestureRecognizer];
// Or [self.view addGestureRecognizer:tapGestureRecognizer];
- (void)handleTap:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded) {
// Animate the view, move it around for a while etc.
// For animating a view use animateWithDuration:animations:
}
}
If I get you correctly I would say that the easiest way to achieve what you request is to create a UIButton in Interface Builder and connect it to an IBAction which moves it to a random spot.. You can then add a custom graphic to the button..
Create a public method in your ViewController with return type IBAction
Create a button in IB and connect its "Touch Up Inside" outlet to your IBAction
I your IBAction method, generate a random x and y coordinate within the screens bounds and animate the movement to this point.
I will/can not go into details on the specific code since it would take way to much space.
Note that your question is very open and vague which is not considered good style on StackOverflow. Also, you might wan't to save stuff like animations until you are a bit more experienced with iOS and Objective-C
-V
Actually Apple made a demo for this.
http://developer.apple.com/library/ios/#samplecode/MoveMe/Introduction/Intro.html
You can try to modify this code to your needs. And the actual functions you where looking for where:
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
If this is the answer you where looking for please click "answered" so this question can be considered as closed :-).
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
is a method called when user touches the view. if you override this in mainview (big view) you will have to find if touched point is where the object is with some helper method as described in your link. else you can override your object's class & implement the method so you dont have to explicitly find if touched point is on the object or not.
for your requirement. i'd say override the uiimageview & inside that put the touchesbegan implementation it will just work fine.
.h file
#interface StarView : UIImageView
#end
.m file
#implementation StarView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// your code in the link with the related methods
Destination = CGPointMake(arc4random() % 320, arc4random() % 480);
// calculate steps based on speed specified and distance between the current location of the sprite and the new destination
xamt = ((Destination.x - self.center.x) / speed);
yamt = ((Destination.y - self.center.y) / speed);
// ask timer to execute moveBall repeatedly each 0.2 seconds. Execute the timer.
mainTimer = [NSTimer scheduledTimerWithTimeInterval:(0.02) target:self selector:#selector(moveBall) userInfo:nil repeats: NO];
}
dont forget to copy the moveBall method after this.
In your mainview just make an instance of StarView & add to mainview