Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
In my OS X Cocoa app, I'd like to display a compass face with degrees and maybe a needle. When the user clicks a heading, I'd like to retreive it.

Anyone know of a component like that? Programming it from scratch is probably beyond my abilities at this time.
 

mduser63

macrumors 68040
Nov 9, 2004
3,042
31
Salt Lake City, UT
I haven't seen anything like that myself. However, I'd encourage you to try creating it yourself. It's not a terribly complex task as custom controls go, but it will be a good challenge for you. Working through it will also teach you a lot about how custom UI is done in Cocoa.

To get you started, the basic steps are:

1. Create a custom NSControl subclass, called say CompassView
2. In that class, override drawRect: and use AppKit and/or CoreGraphics drawing calls to draw your compass.
3. Override the NSResponder mouse handling methods (i.e. at least mouseDown:), and in your override determine which heading the mouse down point is on top of.
4. Design a way for the control to communicate that a heading has been selected. There are multiple ways to do this, but what I would take advantage of NSControl's existing target/action mechanism, by simply calling -sendAction:to: anytime a new heading is selected. Along with that, expose the currently (i.e. most recently) selected heading as a property so it can be retrieved from the sender argument in the action method.
 

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
I haven't seen anything like that myself. However, I'd encourage you to try creating it yourself. It's not a terribly complex task as custom controls go, but it will be a good challenge for you. Working through it will also teach you a lot about how custom UI is done in Cocoa.

To get you started, the basic steps are:

1. Create a custom NSControl subclass, called say CompassView
2. In that class, override drawRect: and use AppKit and/or CoreGraphics drawing calls to draw your compass.
3. Override the NSResponder mouse handling methods (i.e. at least mouseDown:), and in your override determine which heading the mouse down point is on top of.
4. Design a way for the control to communicate that a heading has been selected. There are multiple ways to do this, but what I would take advantage of NSControl's existing target/action mechanism, by simply calling -sendAction:to: anytime a new heading is selected. Along with that, expose the currently (i.e. most recently) selected heading as a property so it can be retrieved from the sender argument in the action method.

Thanks for the suggestions, and show of faith. I've only been at this a few weeks, so pretty raw.:eek: I just don't have a clue (yet) how to do any of the things you mention. I'll take it as a challenge.
 

mduser63

macrumors 68040
Nov 9, 2004
3,042
31
Salt Lake City, UT
Thanks for the suggestions, and show of faith. I've only been at this a few weeks, so pretty raw.:eek: I just don't have a clue (yet) how to do any of the things you mention. I'll take it as a challenge.

Feel free to ask questions as you go. If you do this successfully (or even if you try your best and fail), I can promise that you'll end up learning a lot about how real Cocoa programs are written.
 

ytk

macrumors 6502
Jul 8, 2010
252
5
If I may chime in with a bit of advice here:

The best way to accomplish this is probably to break it down into a series of discrete tasks, as follows:

1) Determine the best way to generate a compass heading from a click. Seems to me the easiest way to do this is to use trigonometry: Given X and Y coordinates relative to a central point, you can find the compass heading by calculating arcsin( Y / sqrt(X^2+Y^2) ). You may have to convert from radians to degrees depending on your math library. So, first write a simple function to convert X and Y coordinates to a compass heading.

2) Now you just need to get X and Y coordinates. You can do this by taking an existing class and creating a subclass, and putting your custom code there. When you click on an object, a call gets made to the object's mouseDown method. So, you need to add a mouseDown method to your object, that handles the incoming message. In this method, you should be able to figure out some way to determine the relative location of the click (I believe it's passed as part of the single parameter to mouseDown, encapsulated in an NSEvent object). However, you'll find that objects tend to have their coordinates start at one corner rather than nicely in the middle (I think it's the lower left by default), so you'll need to convert those coordinates to coordinates relative to the object's center by calculating it's frame size and doing a bit of math.

3) Put your function from the first step into the class from the second step, and call it from the mouseDown handler to determine a compass heading. You should test everything at this point to make sure everything is working. Using printf to check return values from your compass heading function is fine for now. Just make sure everything works as you expect (i.e. when you click on a point in the custom object, the correct compass heading is printed to the debug console).

4) Once this is all working, you need some way to do something with the compass heading. You have a few options here: You can have the custom object send a message to some other designated object with the compass heading as part of it, or you can simply let some other object know that there's a compass heading ready, and provide the most recently selected compass heading as a property that can be queried (as mduser suggested above).

You don't necessarily have to do these steps in order, but I strongly suggest you do so, as solving each of these successive problems will make it more clear what exactly you're trying to accomplish in the next step.

Good luck, and please do feel free to ask questions if you're confused or stuck on anything!
 

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
If I may chime in with a bit of advice here:

The best way to accomplish this is probably to break it down into a series of discrete tasks, as follows:

1) Determine the best way to generate a compass heading from a click. Seems to me the easiest way to do this is to use trigonometry: Given X and Y coordinates relative to a central point, you can find the compass heading by calculating arcsin( Y / sqrt(X^2+Y^2) ). You may have to convert from radians to degrees depending on your math library. So, first write a simple function to convert X and Y coordinates to a compass heading.

2) Now you just need to get X and Y coordinates. You can do this by taking an existing class and creating a subclass, and putting your custom code there. When you click on an object, a call gets made to the object's mouseDown method. So, you need to add a mouseDown method to your object, that handles the incoming message. In this method, you should be able to figure out some way to determine the relative location of the click (I believe it's passed as part of the single parameter to mouseDown, encapsulated in an NSEvent object). However, you'll find that objects tend to have their coordinates start at one corner rather than nicely in the middle (I think it's the lower left by default), so you'll need to convert those coordinates to coordinates relative to the object's center by calculating it's frame size and doing a bit of math.

3) Put your function from the first step into the class from the second step, and call it from the mouseDown handler to determine a compass heading. You should test everything at this point to make sure everything is working. Using printf to check return values from your compass heading function is fine for now. Just make sure everything works as you expect (i.e. when you click on a point in the custom object, the correct compass heading is printed to the debug console).

4) Once this is all working, you need some way to do something with the compass heading. You have a few options here: You can have the custom object send a message to some other designated object with the compass heading as part of it, or you can simply let some other object know that there's a compass heading ready, and provide the most recently selected compass heading as a property that can be queried (as mduser suggested above).

You don't necessarily have to do these steps in order, but I strongly suggest you do so, as solving each of these successive problems will make it more clear what exactly you're trying to accomplish in the next step.

Good luck, and please do feel free to ask questions if you're confused or stuck on anything!


Thanks for the blueprint. That makes the task seem less daunting. :)
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.