Accessing Canvas from Subclass? - objective-c

I am working with a C4 app, and have created a subclass of C4Shape. I am having trouble accessing the canvas from within the subclass but I'm not sure how to check it, or how to get access to it from another object.
This is the code I have so far:
#import "Platform.h"
#implementation Platform {
CGPoint o;
C4Timer *timer;
int speed;
}
-(void) setup {
speed = 10;
[self rect:CGRectMake(0, 0, 100, 100)];
timer = [C4Timer automaticTimerWithInterval:1.0f/30
target:self
method:#"push"
repeats:YES];
o = self.center;
}
+(id) platformWithRange:(CGRect)s {
Platform * bb = [Platform new];
bb.range = s;
return bb;
}
-(void) push {
// check boundaries
o.x-= speed;
if( 0 >= o.x - 50 ) {
o.x = range.size.width;
}
}
#end

Have a look at the second part of this answer: https://stackoverflow.com/a/15885302/1218605
You can create a property on your subclass into which you will set the canvas from the main workspace.
#implemenation C4WorkSpace
-(void)setup {
CustomSubclass *obj = [CustomSubclass new];
obj.canvas = self.canvas;
}
#end

Related

Timer Outputs for Objective-C

This question is a branch off of my previous question from earlier today, but is still a new question. I'm having issues with a timer loop that I declared in the .m file:
// battery level connection timer
- (void)batteryLevelTimerRun
{
batteryLevelSecondsCount = batteryLevelSecondsCount - 1;
int batteryLevelProgress = batteryLevelSecondsCount;
NSString *timerOutput = [NSString stringWithFormat:#"Battery Level: %d%%", batteryLevelProgress];
batteryLevelLabel.text = timerOutput;
float batteryLevelProgressFloat = batteryLevelSecondsCount / 100;
[batteryLevel setProgress:batteryLevelProgressFloat animated:YES];
if (batteryLevelSecondsCount == 20)
{
batteryLevelLabel.textColor = [UIColor orangeColor];
}
else if (batteryLevelSecondsCount == 10)
{
batteryLevelLabel.textColor = [UIColor redColor];
}
else if (batteryLevelSecondsCount == 0)
{
[batteryLevelTimer invalidate];
batteryLevelTimer = nil;
}
}
// battery level connection timer
- (void)batteryLevelSetTimer
{
batteryLevelSecondsCount = 100;
batteryLevelTimer = [NSTimer scheduledTimerWithTimeInterval:9.0 target:self selector:#selector(batteryLevelTimerRun) userInfo:nil repeats:YES];
}
In the .h file, I declared:
#interface MonitoringViewController : UIViewController
{
IBOutlet UILabel *batteryLevelLabel;
IBOutlet UIProgressView *batteryLevel;
NSTimer *batteryLevelTimer;
int batteryLevelSecondsCount;
}
I assigned [self batteryLevelTimer] to a button press. When I ran the code, I got an odd response in the batteryLevelLabel UILabel field. It said Battery Level: -1%. Any idea why this would be? I also setup the UIProgressView to decrement along with label, and that is also dysfunctional (was set to 0), probably because it thinks that the value it was given was -1 so that it defaulted to 0 (UIProgressView value goes from 0.0 to 1.0). Is there some mathematical/logic error I'm missing out on here?
Your code seems ok and works just fine in my test project (I connected button to batteryLevelSetTimer and label counted down to zero as expected). Except for progress bar part -- you should change the line:
float batteryLevelProgressFloat = batteryLevelSecondsCount / 100;
to
float batteryLevelProgressFloat = batteryLevelSecondsCount / 100.0;
because both batteryLevelSecondsCount and 100 are integers, and integer division always yields integer result, which varies between 0 and 1 in your case.

Dynamic Accessibility Label for CALayer

How do I make a CALayer accessible? Specifically, I want the layer to be able to change its label on the fly, since it can change at any time. The official documentation's sample code does not really allow for this.
The following assumes that you have a superview whose layers are all of class AccessableLayer, but if you have a more complex layout this scheme can be modified to handle that.
In order to make a CALayer accessible, you need a parent view that implements the UIAccessibilityContainer methods. Here is one suggested way to do this.
First, have each layer own its UIAccessibilityElement
#interface AccessableLayer : CALayer
#property (nonatomic) UIAccessibilityElement *accessibilityElement;
#end
now in its implementation, you modify the element whenever it changes:
#implementation AccessableLayer
... self.accessibilityElement.accessibilityLabel = text;
#end
The AccessableLayer never creates the UIAccessibilityElement, because the constructor requires a UIAccessibilityContainer. So have the super view create and assign it:
#pragma mark - accessibility
// The container itself is not accessible, so return NO
- (BOOL)isAccessibilityElement
{
return NO;
}
// The following methods are implementations of UIAccessibilityContainer protocol methods.
- (NSInteger)accessibilityElementCount
{
return [self.layer.sublayers count];
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
AccessableLayer *panel = [self.layer.sublayers objectAtIndex:index];
UIAccessibilityElement *element = panel.accessibilityElement;
if (element == nil) {
element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
element.accessibilityFrame = [self convertRect:panel.frame toView:[UIApplication sharedApplication].keyWindow];
element.accessibilityTraits = UIAccessibilityTraitButton;
element.accessibilityHint = #"some hint";
element.accessibilityLabel = #"some text";
panel.accessibilityElement = element;
}
return element;
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
int numElements = [self accessibilityElementCount];
for (int i = 0; i < numElements; i++) {
if (element == [self accessibilityElementAtIndex:i]) {
return i;
}
}
return NSNotFound;
}

Objective-C malloc with c array of array

I have as property in a view, an array of array like this:
#interface MyView : UIView
#property (nonatomic) CGPoint **matrix;
#end
in the controller that own this view I have load the data in the -viewDidLoad and free memory in the -viewDidUnload like this:
- (void)viewDidLoad
{
self.myView.matrix = malloc(sizeof(CGPoint*) * array1Size);
for (int k = 0; k < array1Size; k++) {
self.myView.matrix[k] = malloc(sizeof(CGPoint) * innerArraySize);
}
}
- (void)viewDidUnload
{
for (int k = 0; k < array1Size; k++) {
free(self.myView.matrix[k]);
self.myView.matrix[k] = nil;
}
free(self.myView.matrix);
self.myView.matrix = nil;
[self setMyView:nil];
[super viewDidUnload];
}
While profiling I see a leak here. Can someone help me where I'm wrong?
thanks
update:
i try to remove free code from viewDidUnload and use dealloc like this:
-(void)dealloc {
[self freeArray];
}
- (void) freeArray {
for (int k = 0; k < 5; k++) {
free(self.myView.matrix[k]);
self.myView.matrix[k] = NULL;
}
free(self.myView.matrix);
self.myView.matrix = NULL;
}
then I embed init code in:
if (self.graphView.matrix == NULL) {
...
}
now no more leak, THANKS!
Your code does not make sure that each setup of the matrix is matched by exactly one tear down. For example, viewDidUnload is not guaranteed to be called. Also, you have no guards against duplicate setup or tear down.
If you really need the C array of arrays, a better approach would be to create it in the initializer of the view (initWithFrame: or initWithCoder:) and remove it in its dealloc.
Edit: To alleviate your concerns regarding dealloc and ARC:
You can certainly override dealloc in ARC and rely on it being called. The only difference is that you cannot explicitly call the overridden implementation ([super dealloc]). ARC will insert that for you.
Use an array of CGPoint:
#property (nonatomic) CGPoint *matrix;
.... init ...
{
...
matrix = (CGPoint*) malloc(arraySize * sizeof(CGPoint));
...
}
- (void) dealloc
{
free(matrix);
}
Then you simply set/get CGPoint structures directly in the array:
CGPoint someCGPoint = {0,0};
matrix[i] = someCGPoint;
someCGPoint = matrix[i];
That'll be faster and more memory efficient than CGPoint**. And your leak is gone.

Sparrow - how to fix SIGABRT error, Game initwithwitdth

I've just started with the sparrow framework, and have been following "The Big Sparrow Tutorial" by Gamua themselves. I'm on the first part of the tutorial, using the AppScaffold 1.3 but when I try to compile my basic code it hangs at the loading screen and gives me a SIGABRT error.
I put an exception breakpoint, and it stopped here, in GameController.m (seen at bottom) of the AppScaffold:
mGame = [[Game alloc] initWithWidth:gameWidth height:gameHeight];
This was also my only output:
2012-07-30 07:19:54.787 AppScaffold[1682:10a03] -[Game initWithWidth:height:]: unrecognized selector sent to instance 0x7553980
(lldb)
I am using the stock AppScaffold, the only thing I changed was the Game.m.
This is my Game.m:
#interface Game : SPSprite
#end
#implementation Game
{
#private
SPImage *mBackground;
SPImage *mBasket;
NSMutableArray *mEggs;
}
- (id)init
{
if((self = [super init]))
{
//load the background image first, add it to the display tree
//and keep it for later use
mBackground = [[SPImage alloc] initWithContentsOfFile:#"background.png"];
[self addChild:mBackground];
//load the image of the basket, add it to the display tree
//and keep it for later use
mBasket = [[SPImage alloc] initWithContentsOfFile:#"basket.png"];
[self addChild:mBasket];
//create a list that will hold the eggs,
//which we will add and remove repeatedly during the game
mEggs = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc
{
[mBackground release];
[mBasket release];
[mEggs release];
[super dealloc];
}
#end
I've tried my best to use my basic troubleshooting tactics, but I'm very new to Obj-C and Sparrow and could use a hand :)
Thanks
EDIT: I've addded the GameController.m contents here for clarity:
//
// GameController.m
// AppScaffold
//
#import <OpenGLES/ES1/gl.h>
#import "GameController.h"
#interface GameController ()
- (UIInterfaceOrientation)initialInterfaceOrientation;
#end
#implementation GameController
- (id)initWithWidth:(float)width height:(float)height
{
if ((self = [super initWithWidth:width height:height]))
{
float gameWidth = width;
float gameHeight = height;
// if we start up in landscape mode, width and height are swapped.
UIInterfaceOrientation orientation = [self initialInterfaceOrientation];
if (UIInterfaceOrientationIsLandscape(orientation)) SP_SWAP(gameWidth, gameHeight, float);
mGame = [[Game alloc] initWithWidth:gameWidth height:gameHeight];
mGame.pivotX = gameWidth / 2;
mGame.pivotY = gameHeight / 2;
mGame.x = width / 2;
mGame.y = height / 2;
[self rotateToInterfaceOrientation:orientation animationTime:0];
[self addChild:mGame];
}
return self;
}
- (void)dealloc
{
[mGame release];
[super dealloc];
}
- (UIInterfaceOrientation)initialInterfaceOrientation
{
// In an iPhone app, the 'statusBarOrientation' has the correct value on Startup;
// unfortunately, that's not the case for an iPad app (for whatever reason). Thus, we read the
// value from the app's plist file.
NSDictionary *bundleInfo = [[NSBundle mainBundle] infoDictionary];
NSString *initialOrientation = [bundleInfo objectForKey:#"UIInterfaceOrientation"];
if (initialOrientation)
{
if ([initialOrientation isEqualToString:#"UIInterfaceOrientationPortrait"])
return UIInterfaceOrientationPortrait;
else if ([initialOrientation isEqualToString:#"UIInterfaceOrientationPortraitUpsideDown"])
return UIInterfaceOrientationPortraitUpsideDown;
else if ([initialOrientation isEqualToString:#"UIInterfaceOrientationLandscapeLeft"])
return UIInterfaceOrientationLandscapeLeft;
else
return UIInterfaceOrientationLandscapeRight;
}
else
{
return [[UIApplication sharedApplication] statusBarOrientation];
}
}
- (void)rotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
animationTime:(double)animationTime
{
float angles[] = {0.0f, 0.0f, -PI, PI_HALF, -PI_HALF};
float oldAngle = mGame.rotation;
float newAngle = angles[(int)interfaceOrientation];
// make sure that rotation is always carried out via the minimal angle
while (oldAngle - newAngle > PI) newAngle += TWO_PI;
while (oldAngle - newAngle < -PI) newAngle -= TWO_PI;
// rotate game
if (animationTime)
{
SPTween *tween = [SPTween tweenWithTarget:mGame time:animationTime
transition:SP_TRANSITION_EASE_IN_OUT];
[tween animateProperty:#"rotation" targetValue:newAngle];
[[SPStage mainStage].juggler removeObjectsWithTarget:mGame];
[[SPStage mainStage].juggler addObject:tween];
}
else
{
mGame.rotation = newAngle;
}
// inform all display objects about the new game size
BOOL isPortrait = UIInterfaceOrientationIsPortrait(interfaceOrientation);
float newWidth = isPortrait ? MIN(mGame.gameWidth, mGame.gameHeight) :
MAX(mGame.gameWidth, mGame.gameHeight);
float newHeight = isPortrait ? MAX(mGame.gameWidth, mGame.gameHeight) :
MIN(mGame.gameWidth, mGame.gameHeight);
if (newWidth != mGame.gameWidth)
{
mGame.gameWidth = newWidth;
mGame.gameHeight = newHeight;
SPEvent *resizeEvent = [[SPResizeEvent alloc] initWithType:SP_EVENT_TYPE_RESIZE
width:newWidth height:newHeight animationTime:animationTime];
[mGame broadcastEvent:resizeEvent];
[resizeEvent release];
}
}
// Enable this method for the simplest possible universal app support: it will display a black
// border around the iPhone (640x960) game when it is started on the iPad (768x1024); no need to
// modify any coordinates.
/*
- (void)render:(SPRenderSupport *)support
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
glEnable(GL_SCISSOR_TEST);
glScissor(64, 32, 640, 960);
[super render:support];
glDisable(GL_SCISSOR_TEST);
}
else
[super render:support];
}
*/
#end
Here is my Xcode project: http://cl.ly/2e3g02260N47
You are calling a
initWithWidth:height:
method, while none is defined in your class.
From your edit, it seems that the initWithWidth method is declared in the class GameController, not in Game.
So, it seems that the
In which context are you calling initWithWidth:height: method is declared in Game.h but you define it in GameController.m.
This explains both why you get the SIGABRT and the errors when compiling.
The fix is calling
mGame = [[GameController alloc] init];
from GameController initWithWidth...
- (id)initWithWidth:(float)width height:(float)height
{
if ((self = [super initWithWidth:width height:height]))
{
float gameWidth = width;
float gameHeight = height;
// if we start up in landscape mode, width and height are swapped.
UIInterfaceOrientation orientation = [self initialInterfaceOrientation];
if (UIInterfaceOrientationIsLandscape(orientation)) SP_SWAP(gameWidth, gameHeight, float);
mGame = [[Game alloc] init];
mGame.pivotX = gameWidth / 2;
mGame.pivotY = gameHeight / 2;
mGame.x = width / 2;
mGame.y = height / 2;
[self rotateToInterfaceOrientation:orientation animationTime:0];
[self addChild:mGame];
}
return self;
}
The tutorial was very old and was incompatible with the latest scaffold;
I did this:
- (id)init
{
if((self = [super init]))
{
when I should've done this:
- (id)initWithWidth:(float)width height:(float)height
{
if ((self = [super initWithWidth:width height:height]))
{
thanks, though sergio!
(There are much better sparrow tutorials and I'm even making my own video tutorials :P)

Objective-C Method to mute and unmute volume

I'm a little stuck, i'm trying to write a method which when a button is pressed; will get the current volume of iTunes, store the volume as an int declared as x. Then make the iTunes volume equal to 0, which will essentially mute the iTunes volume, but then i want the iTunes volume to return to the int x if the button is pressed again, which will essentially unmute the iTunes volume and restore it to the original volume.
Here's what i have so far:
- (IBAction)muteAndUnmute:(id)sender {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
int x;
x = [iTunes soundVolume];
if ([iTunes soundVolume] > 0 ) {
[volumeSlider setIntValue:0];
[iTunes setSoundVolume:[volumeSlider intValue]];
[volumeLabel setIntValue:[volumeSlider intValue]];}
}
Any help would be much appreciated, i think it's quite easy to do but just can't get my head around it, thanks in advance, Sami.
make your volume value (vol) as class variable and not local, then
// MyWindowController.h
#interface MyWindowController : NSWindowController {
int vol;
}
- (IBAction)btnPressed:(id)sender;
#end
// MyWindowController.m
#implementation MyWindowController
- (id)init {
if (self = [super init]) {
vol = 0;
}
return self;
}
- (IBAction)btnPressed:(id)sender
{
id iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
if ([iTunes soundVolume] > 0 )
{
vol = [iTunes soundVolume];
[iTunes setSoundVolume:0];
}
else
[iTunes setSoundVolume:vol];
}
#end
you can use this:
fVolume = [MPMusicPlayerController iPodMusicPlayer].volume;
[MPMusicPlayerController iPodMusicPlayer].volume = 0.0;
//do whatever you want
[MPMusicPlayerController iPodMusicPlayer].volume = fVolume;