I want to put together a ping pong game in Xcode with Sprite Kit. But I want to do it for Mac OS X, there are no tutorials on fixing my code, and I want to make a keydown event to move the paddle. Here's my code.
-(void)keyDown:(NSEvent *)theEvent {
/* Called when a keyPress occurs */
// inside code
}
EDIT:
So I downloaded a pong game that is an Xcode project then I looked at it and saw this:
- (void)handleKeyEvent:(NSEvent*)keyEvent keyDown:(BOOL)isKeyDown {
if ([keyEvent keyCode] == LED_PONG_MOVE_UP || [keyEvent keyCode] == LED_PONG_MOVE_UP_ALT) {
self.moveUp = isKeyDown;
} else if ([keyEvent keyCode] == LED_PONG_MOVE_DOWN || [keyEvent keyCode] == LED_PONG_MOVE_DOWN_ALT) {
self.moveDown = isKeyDown;
}
}
and this:
#define LED_PONG_MOVE_UP 13 // W
#define LED_PONG_MOVE_UP_ALT 126 // Arrow Up
#define LED_PONG_MOVE_DOWN 1 // S
#define LED_PONG_MOVE_DOWN_ALT 125 // Arrow Down
So that about solves it for anyone in my place, fellow iOS haters, and Mac OS X lovers.
Notify me for more info if you need help.
It sounds like you simply need to make certain that your SKScene is designated as the first responder, which means that all events come in through your SKScene first.
So when your game (or scene) starts up, make certain that your NSWindow calls "makeFirstResponder:" with your SKScene object as the parameter.
Related
In macOS 10.14 users can choose to adopt a system-wide light or dark appearance and I need to adjust some colours manually depend of the current mode.
Since the actual appearance object you usually get via effectiveAppearance is a composite appearance, asking for its name directly probably isn't a reliable solution.
Asking for the currentAppearance usually isn't a good idea, either, as a view may be explicitly set to light mode or you want to know whether a view is light or dark outside of a drawRect: where you might get incorrect results after a mode switch.
The solution I came up with looks like this:
BOOL appearanceIsDark(NSAppearance * appearance)
{
if (#available(macOS 10.14, *)) {
NSAppearanceName basicAppearance = [appearance bestMatchFromAppearancesWithNames:#[
NSAppearanceNameAqua,
NSAppearanceNameDarkAqua
]];
return [basicAppearance isEqualToString:NSAppearanceNameDarkAqua];
} else {
return NO;
}
}
You would use it like appearanceIsDark(someView.effectiveAppearance) since the appearance of a specific view may be different than that of another view if you explicitly set someView.appearance.
You could also create a category on NSAppearance and add a - (BOOL)isDark method to get someView.effectiveAppearance.isDark (better chose a name that is unlikely to be used by Apple in the future, e.g. by adding a vendor prefix).
I have used the current appearance checking if the system is 10.14
+ (BOOL)isDarkMode {
NSAppearance *appearance = NSAppearance.currentAppearance;
if (#available(*, macOS 10.14)) {
return appearance.name == NSAppearanceNameDarkAqua;
}
return NO;
}
And to detect the change of mode in a view the methods are:
- (void)updateLayer;
- (void)drawRect:(NSRect)dirtyRect;
- (void)layout;
- (void)updateConstraints;
And to detect the change of mode in a view controller the methods are:
- (void)updateViewConstraints;
- (void)viewWillLayout;
- (void)viewDidLayout;
Using notification:
// Monitor menu/dock theme changes...
[NSDistributedNotificationCenter.defaultCenter addObserver:self selector:#selector(themeChanged:) name:#"AppleInterfaceThemeChangedNotification" object: nil];
-(void)themeChanged:(NSNotification *) notification {
NSLog (#"%#", notification);
}
For more information Dark Mode Documentation
Swift 4
func isDarkMode(view: NSView) -> Bool {
if #available(OSX 10.14, *) {
return view.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
}
return false
}
For me neither of these answers worked, if I wanted a global state, not per view, and I didn't have access to the view, and I wanted to be notified for updates.
The solution was to ask for NSApp.effectiveAppearance in the main thread, or at least after the current callback method has returned to the system.
So, first I have to register, following the directions of Saúl Moreno Abril, with a code like
[NSDistributedNotificationCenter.defaultCenter addObserver:self selector:#selector(themeChanged:) name:#"AppleInterfaceThemeChangedNotification" object: nil];
then on the callback method write something like
-(void)themeChanged:(NSNotification *) notification {
[self performSelectorOnMainThread:#selector(themeChangedOnMainThread) withObject:nil waitUntilDone:false];
}
and then the actual code:
- (void) themeChangedOnMainThread {
NSAppearance* appearance = NSApp.effectiveAppearance;
NSString* name = appearance.name;
BOOL dark = [appearance bestMatchFromAppearancesWithNames:#[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua;
}
Also the answer from Borzh helped, but is seemed more fragile than the others.
There are actually 8 possible appearances for a view, and 4 of them are for ordinary use. That is,
NSAppearanceNameAqua the Light Mode,
NSAppearanceNameDarkAqua the Dark Mode,
NSAppearanceNameAccessibilityHighContrastAqua Light Mode with increased contrast (set from Accessibility),
NSAppearanceNameAccessibilityHighContrastDarkAqua Dark Mode with increased contrast.
A direct comparison
appearance.name == NSAppearanceNameDarkAqua;
may fail to detect the dark mode if it is with increased contrast. So, always use bestMatchFromAppearancesWithNames instead.
And it is even better to take account of the high-contrast appearances for better accessibility.
To know if the app appearance is Dark, use next code:
+ (BOOL)isDarkMode {
NSString *interfaceStyle = [NSUserDefaults.standardUserDefaults valueForKey:#"AppleInterfaceStyle"];
return [interfaceStyle isEqualToString:#"Dark"];
}
I am writing an audio plugin (VST) in Objective-C on OSX. My plugin gets loaded into an application and is given an NSWindow in which to add my own NSView. I need to be able to intercept keyboard events on the NSWindow which I can partly do, but not fully.
Here's what I have tried so far:
Make sure my view is the first response and handle keyDown events. This works for most keyDown events, but not carriage return or special keys like cut/copy/paste.
Use addLocalMonitorForEventsMatchingMask. This doesn't provide anything more useful than keyDown.
The NSWindow I'm given has a menu with some key equivalents for cut/copy/paste. I occasionally need to intercept these if the user has something selected in my NSView. I also occasionally need to intercept carriage return if the user is entering some data.
My UI is rendered using OpenGL so I'm not using standard Cocoa UI components apart from NSView to host my OpenGL surface.
I don't want the user to have to enable anything special to do this, like accessibility.
In my keyDown handler I have something like this:
- (void)keyDown:(NSEvent*)event
{
NSString* s = event.charactersIgnoringModifiers;
unichar modified_key = 0;
if (s && [s length] > 0)
{
modified_key = [s characterAtIndex:0];
}
if (modified_key == NSCarriageReturnCharacter)
{
// carriage return
}
}
This works in a stand alone application, but fails when it's hosted as an audio plugin. The problem I think is that the application hosting the plugin is intercepting events before they reach my event handlers.
You seem to be handling the event in a strange way. Process the NSEvent keyCode directly. You can use modifierFlags to get the modifiers.
This won't work for certain "system" key combinations (like Command + Space) for which you will need accessibility access.
- (void)keyDown:(NSEvent *)event
{
if (event.keyCode == 36)
NSLog(#"you pressed return!");
if (event.modifierFlags & NSEventModifierFlagCommand)
{
if (event.keyCode == 8)
NSLog(#"you pressed command+c!");
}
}
I've run into an issue with this little Objective-C project I'm doing and it's proving to be a bit of a roadblock. I'm playing around with Apple's NSSpeechRecognizer software on El Capitan, and I'm trying to get this guy running properly so that when the riddle I give it is posed to the user, the user can respond with a word to "do something cool". As it stands right now, the delegate method:
-(void) speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(NSString *)command { ... }`
is never even called, even though it appears the recognition icon is correctly detecting the answer to the riddle.
The problem is that your main function has a loop that is continually checking whether the speech has been recognizing. You are not giving NSSpeechRecognizer a chance to actually deliver any messages to you.
Your app needs to let the main "run loop" run, so it can deliver messages. Normally, in an OS X app, your main would just call NSApplicationMain, which does this for you.
Your code is effectively this:
#interface RecognizerDelegate : NSObject <NSSpeechRecognizerDelegate>
#property (nonatomic) NSSpeechRecognizer *recognizer;
#property (nonatomic) BOOL didRecognize;
#end
#implementation RecognizerDelegate
- (id)init
{
if ((self = [super init])) {
self.didRecognize = NO;
self.recognizer = [[NSSpeechRecognizer alloc] init];
self.recognizer.listensInForegroundOnly = NO;
self.recognizer.blocksOtherRecognizers = YES;
self.recognizer.delegate = self;
self.recognizer.commands = #[ #"hello" ];
[self.recognizer startListening];
}
return self;
}
- (void)speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(NSString *)command
{
self.didRecognize = YES;
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool
{
RecognizerDelegate *recognizerDelegate = [[RecognizerDelegate alloc] init];
while (recognizerDelegate.didRecognize == NO) {
// do nothing
}
NSLog(#"Recognized!");
}
return 0;
}
That while loop is doing nothing useful, just running your CPU in a loop and wasting time and energy. You are not letting any other code in NSSpeechSynthesizer, or any of the system frameworks like Foundation or AppKit, get the chance to do anything. So, nothing happens.
To fix this in the short term: you can let the main run loop run for a little while in each pass through the loop. This code would let the system run for a second, then would return to your code, so you could check again:
while (recognizerDelegate.didRecognize == NO) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
The longer-term fix would be to move your code out of main and to structure it like a real OS X app. Instead of using a loop to poll a condition like recognizerDelegate.didRecognize, you would just trigger the "next thing" directly from delegate methods like -speechRecognizer:didRecognizeCommand:, or you would use things like NSTimer to run code periodically.
For more details, see the Apple doc Cocoa Application Competencies for OS X, specifically the "Main Event Loop" section.
I had the same problem using NSSpeechRecognizer. The callback function:
func speechRecognizer(_ sender: NSSpeechRecognizer,
didRecognizeCommand command: String) {}
...was never called, even though everything appeared to be working.
There were three things I changed to get the code working.
1) I had to enable the entitlement in my "sandboxed" mode application to allow for microphone use.
... I also did these other two things, as well.
2) I added the "Privacy - Microphone Usage Description" in the info.pList, and set the string value to "I want to listen to you speak"
3) I added the "Privacy - Speech Recognition Usage Description" in the info.pList, and set the string value to "I want to write down what you say"
I have recently decided to start a project which requires some kind of direct input to control my application and, in my case, I decided to use my Logitech 3D Extreme PRO joystick.
Therefore I started documenting myself online using the HID Class Device Interface Guide provided by Apple (Even the one dealing with the new HID Manager for OS X 10.5). I was able to implement the very main methods but unfortunately none of them worked.
After few days of search I have discovered a great Library developed by Daij-Djan : DDHidLib which helps a lot when dealing with direct inputs, providing great methods for discovering button presses and stick toggles, queues and lot more.
Even though this lib is a bit odd (2007), I decided to import it and give it a try..
I imported it into my project and started implementing some of it's methods which apparently seem very easy.
For instance, inside the DDHidJoystick sub-class, I found:
- (void)ddhidJoystick:(DDHidJoystick *)joystick buttonDown:(unsigned)buttonNumber
This method returns the number of the button that have been pressed.
Now, after this long introduction, let me explain my problem:
I coded a tested implementation of this class but without success (At least with my hardware).
Apparently with no reason the method reported above gets called only if I press one button on my joystick (Number 7).
Therefore I get some kind of notification {NSLog()} just in that case, even though the library is deigned to retrive any kind of input from the device.
But the most weird thing is that I am able to retrive all the other buttons/povs/sticks values only by pressing that specific button (N. 7) at the same time.
So let's say I want to get input from button 8, I will have to press button 8, than, at the same time, button number 7. Now i got a notification for both inputs.
If I want to get x-axis value, I need to:
Move x-stick
Press button n. 7
Then I see one notification both for button n.7 and x-axis moved at the same time.
To clarify, let me introduce this method:
- (void)ddhidJoystick:(DDHidJoystick *)joystick
stick:(unsigned)stick
xChanged:(int)value
As you can imagine, this method should be triggered whenever I move my X-Axis stick, however it doesn't.
Instead it gets triggered only if I press button number 7 and then, at the same time,I move my stick !
I tested out the joystick with X-Plane 10 and works just fine, so my guess is that there should be something else different from my app acquiring the input and hiding it.
I'm expecting to move my axis-sticks and se a NSLog, but that is not happening.
I'm not requesting a specific response on how to achieve my task using this lib, any other working approach will really be appreciated.
I really hope that this question is not too specific and could be helpful to somebody else in the future since nobody (apparently) tried to implement such input.
Thanks a lot to anyone who will reply to this post.
For the most curious:
I am building my own quad-copter using Arduino/Raspberry and lot of other electronics. I got a TX/RX Module operating at 2.4GHz which allows communication between 2 boards: one on the quad, and the other one plugged to the pc. I developed a lib (in C) reading POSIX documentation to read/write to serial ports and therefore I am able to send data over usb to my board, which than sends it to the quad. Finally I'am developing an OS X app to control the copter using the mentioned hardware/software and it is not far from being finished.
However for my purposes I want to use my joystick, and this is difficult.
In the end I will have a live video from onboard (FPV-Like) on the screen with telemetry all controlled by my Logitech 3D Extreme.
EDIT - I FOUND A SOLUTION
I found a solution and it seems to work pretty good!
Basically I had to edit a bit one method of the lib, adding support for the engine slider the joystick has.
Open up DDHidJoystick.m
Locate the method - (BOOL) addElement: (DDHidElement *) element;
Add the case statement case kHIDUsage_GD_Slider:
Set the action to [mStickElements addObject: element];
I will post the code here just in case somebody needs it in the future:
- (BOOL) addElement: (DDHidElement *) element;
{
DDHidUsage * usage = [element usage];
if ([usage usagePage] != kHIDPage_GenericDesktop)
return NO;
BOOL elementAdded = YES;
switch ([usage usageId])
{
case kHIDUsage_GD_X:
if (mXAxisElement == nil)
mXAxisElement = [element retain];
else
[mStickElements addObject: element];
break;
case kHIDUsage_GD_Y:
if (mYAxisElement == nil)
mYAxisElement = [element retain];
else
[mStickElements addObject: element];
break;
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
[mStickElements addObject: element];
break;
case kHIDUsage_GD_Hatswitch:
[mPovElements addObject: element];
break;
/* EDIT HERE */
case kHIDUsage_GD_Slider:
[mStickElements addObject: element];
default:
elementAdded = NO;
}
return elementAdded;
}
Under this line you can find my whole implementation, and an image of the joystick.
(Developing on OS X 10.10 - Alberto Bellini)
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self startWatchingJoysticks];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (void)startWatchingJoysticks
{
//get an array of all joystick objects
joySticks = [[DDHidJoystick allJoysticks] retain];
//become the delegate of all available joystick objects
[joySticks makeObjectsPerformSelector:#selector(setDelegate:) withObject:self];
[joySticks makeObjectsPerformSelector:#selector(startListening) withObject:nil];
}
- (void)dealloc
{
[joySticks release];
[super dealloc];
}
//these are the DDHidLib joystick delegate methods related to buttons
- (void)ddhidJoystick:(DDHidJoystick *)joystick buttonDown:(unsigned)buttonNumber
{
NSLog(#"button down: %d", buttonNumber);
//Works only number 7
}
- (void)ddhidJoystick:(DDHidJoystick *)joystick
stick:(unsigned)stick
xChanged:(int)value
{
NSLog(#"x axis %d",value);
//Works ONCE only if presing button number 7 and moving stick
}
- (void)ddhidJoystick:(DDHidJoystick *)joystick
stick:(unsigned)stick
yChanged:(int)value
{
NSLog(#"y axis %d",value);
//Works ONCE only if presing button number 7 and moving stick
}
- (void)ddhidJoystick:(DDHidJoystick *)joystick
stick:(unsigned)stick
otherAxis:(unsigned)otherAxis
valueChanged:(int)value
{
NSLog(#"z axis %d",value);
//Works ONCE only if presing button number 7 and moving stick
}
- (void)ddhidJoystick:(DDHidJoystick *)joystick
stick:(unsigned)stick
povNumber:(unsigned)povNumber
valueChanged:(int)value
{
NSLog(#"Pov changed");
//Works ONCE only if presing button number 7 and moving stick
}
#end
Maybe I found the problem with your joystick.
Logitech Extreme 3D has no standard data packet for joysticks. It has different HID report descriptor and I can not found HID report descriptor parser in DDHidLib. I think DDHidLib just assume standard data packet.
Check this link: http://www.circuitsathome.com/mcu/using-logitech-extreme-3d-pro-joystick-with-arduino-hid-library
Unfortunately, I can not help you more because I don't know nothing about objetive-c nor OSX nor HID.
Maybe you can modify data packet structure in DDHidLib, create a HID report descriptor parser for DDHidLib or get a new joystick with standard data packet. ;)
I am writing an iOS app for the iPhone in Xcode and I have created some classes as well as their methods inside their respective .h and .m files (that's two classes so basically I have two pairs of .h & .m files)
I now I want to start writing my main loop that will be executed whenever the user hits on the play button, but where exactly do I do that ?
Do I do that in ViewController.m ? e.g. inside this method :
- (IBAction)playPressed:(UIButton *)sender
{
// main loop executed in here ?
// or simply message to the main loop to start executing is *sent* from here ?
}
I read about a similar question in here and someone was suggesting AppDelegate. Now would that be AppDelegate.m or AppDelegate.h ? And if that's the case do I just start writing code or do I include everything inside something like :
int main(int argc, char **argv)
{
....
}
in the Appdelegate file?
I tried to simply start instantiating classes and declaring generic methods (not belonging to any particular class that is..) in a game.m file I created and I get a initializer element is not a compile-time constant warning as soon as I try instantiating anything
Any help? Coming from c++ it would really help me to clarify once and for all in which file exactly to write my main loop and whether I should wrap it in some kind of an int main() function..
thanks!
PS :
Just in case it makes any difference, my ViewController will only consist of a play button that would start the execution of my main loop whenever its pressed, and a stop button that would terminate the execution of the main loop
I have created their respective methods in ViewController.m :
- (IBAction)playPressed:(UIButton *)sender
{
//
}
- (IBAction)stopPressed:(UIButton *)sender
{
// ??
}
which are for the time being empty :)
The programming methodoly on iOS is different from the C++ methodoly.
In C++ , indeed , you would have to make an infinite loop and get the touches , draw everything , etc at each frame.
Until the player presses "exit" and you break the loop.
On iOS , things are done differently:
You already have a main.m file in which you have a main function.
That starts up the app delegate. That app delegate tells you when the app finished launching , goes to background , comes in foreground , etc.
When the app finished launching , you go to your first actual screen.
There , you ADD subviews. You don't draw them at each frame. That is done automatically for you once you have added the view to a parent view.
The programming on iOS is based on events. You don't have to check for touches and see if the
touch location is on a button and then call the method of that button.
Instead , you set a callback method for the button and it's called automatically for you once the button is pressed.
Of course , you first need to alloc the button and add it to a parent view.
Once you get used to this event based programming model , you will for sure like it.
At the start it may seam very different and maybe it doesn't make sense to you , but don't worry.
Comming from a C++ background is surely a good start.
Cheers,
George
EDIT: In that case , I can give more specific info:
So , you go from the AppDelegate in your first screen. Let's call it MainAppScreen.
Now , you need to add those 2 buttons and set selectors ( callback methods ) for them. I can see you already did that.
Now :
- (IBAction)playPressed:(UIButton *)sender
{
running = TRUE;
[self performSelectorInBackground:#selector(myLoop) withObject:nil];
}
- (IBAction)stopPressed:(UIButton *)sender
{
running = FALSE;
}
- (void) myLoop
{
while(running)
{
//this is your loop. You can code in here.
}
}
Where running is an instance variable in the MainAppScreen class.
Hope this helps.
Cheers!
Every iOS app, as well as every executable file has an entry point - this is the main(). You can't have more than one entry points of an executable.And if you look closely into the project you will see that there is an automatically generated main.m file in the Supporting Files group in Xcode's navigator, which looks like this:
#import <UIKit/UIKit.h>
#import "MyAppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));
}
}
What you want to do is not clear enough, but it's a good start reading about the structure and the lifecycle of iOS apps, objective-c syntax, get familiar with the UIKit and at least some of the frameworks Apple provide.
You don't have a main in iOS apps (well, technically you do have a main, but you don't have to worry about writing it). That's all handled for you. The runloop is all done for you too. All you have to do is create your button and then tell it (via addTarget method) which method to run when it gets pressed.
Update:
This is pseudo(ish) code for what you'd need to do....
[startButton addTarget:#selector(startPressed:)];
[stopButton addTarget:#selector(stopPressed:)];
-(void)startPressed {
backgroundThread = [[NSThread alloc] initWithWhateverYouWantToRun];
[backgroundThread start];
}
-(void)stopPressed {
[backgroundThread stop];
}
In your background thread, if you want to update the UI, you would call sendMessageOnMainThread (or something similar - can't remember the exact details at the moment!)