Sparrow - how to fix SIGABRT error, Game initwithwitdth - objective-c

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)

Related

Accessing Canvas from Subclass?

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

Objective-C init function not initializing correctly

#interface Set : NSObject
{
// instance variables
int repetitions;
int weight;
}
// functions
- (id)init;
- (id)initWithReps: (int)newRepetitions andWeight: (int)newWeight;
#implementation Set
-(id)init
{
if (self = [super init]) {
repetitions = 0;
weight = 0;
}
return self;
}
-(id)initWithReps: (int)newRepetitions andWeight: (int)newWeight
{
if (self = [super init])
{
repetitions = newRepetitions;
weight = newWeight;
}
return self;
}
#implementation eFit2Tests
- (void)setUp
{
[super setUp];
// Set-up code here.
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testInitWithParam
{
Set* test = nil;
test = [test initWithReps:10 andWeight:100];
NSLog(#"Num Reps: %d", [test reps]);
if([test reps] != 10) {
STFail(#"Reps not currectly initialized. (initWithParam)");
}
NSLog(#"Weight: %d", [test weight]);
if([test weight] != 100) {
STFail(#"Weight not currectly initialized. (initWithParam)");
}
}
For some reason the test at the bottom of this code snippet fails because the values of repetitions and weight are always equal to 0. I come from a background in Java and am clueless as to why this is the case. Sorry for the silly question...
You are setting test to nil, and then sending it initWithReps:andWeight:. This is equivalent to [nil initWithReps:10 andWeight:100], which obviously isn't what you want. nil just responds to any message with itself or 0, so that init message is returning nil and sending reps to nil is returning 0.
To create an object, you want the alloc class method — i.e. Set *test = [[Set alloc] initWithReps:10 andWeight:100]. (And if you're not using ARC, you'll want to release this object when you're finished with it, per the memory management guidelines.)
Where you're initializing your set, replace it with:
Set *test = [[Set alloc] initWithReps: 10 andWeight: 100];
You're getting 0 because that's the default return from a nil object (you've initialized test to nil) - there are no NullPointerExceptions in Objective-C

After xcode crash some buttons have either empty titles or are partially truncated

This seems to be a partial repost of XCode 4: some button titles not visible in iOS simulation but their question wasn't answered.
Please be gentle, I'm very new to this.
I've implemented a stack-based calculator program as per the stamford lecture series, but some of my buttons show as either empty or half truncated in the simulator. The same buttons will do it every time I run the sim, but if I move any buttons around it changes which ones are affected. It's usually the bottom ones.
First example here: http://i.imgur.com/YpC1f.png - see how the bottom row of buttons don't display correctly? If I make any one of those buttons taller, it will show with no title but all the other four buttons will then show correctly.
I thought it might be too close to the bottom, or those buttons were broken, or similar. So I deleted the whole bottom row, made everything smaller, and then recreated those buttons and now I get four buttons with blank titles and two truncated: http://i.imgur.com/kM1Rb.png
Note that the buttons all still work as expected, it's just the display that isn't right.
Am I doing something wrong? Any advice appreciated.
EDIT: Full code from the controller:
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController()
#property (nonatomic) BOOL userIsInTheMiddleOfEnteringANumber;
#property (nonatomic, strong) CalculatorBrain *brain;
- (void)updateStackDisplay:(NSString *)value;
#end
#implementation CalculatorViewController
#synthesize display = _display;
#synthesize stackDisplay = _stackDisplay;
#synthesize userIsInTheMiddleOfEnteringANumber = _userIsInTheMiddleOfEnteringANumber;
#synthesize brain = _brain;
- (CalculatorBrain *)brain {
if (!_brain) _brain = [[CalculatorBrain alloc] init];
return _brain;
}
- (void)updateStackDisplay:(NSString *)value {
// if there's nothing sent so far, just initialise it
if (self.stackDisplay.text.length == 0) {
self.stackDisplay.text = value;
return;
}
// This part is a little confusing. Extra assignment asked for = to be added to the end of the stack label if an operation was pressed.
// Here I check for = at the end of the label and remove it so it's only displayed once. If "=" is being passed (done by the operation itself), it will be added back on right at the end of this function.
if ([self.stackDisplay.text rangeOfString:#"="].location == (self.stackDisplay.text.length - 1)) {
// .location starts at zero, .length doesn't.
self.stackDisplay.text = [self.stackDisplay.text substringToIndex:[self.stackDisplay.text length]-1];
}
// If we add a space after remove the = we'll end up with double space. Hence the else if, not if.
else if (!self.userIsInTheMiddleOfEnteringANumber) {
self.stackDisplay.text = [self.stackDisplay.text stringByAppendingString:#" "];
}
self.stackDisplay.text = [self.stackDisplay.text stringByAppendingString:value];
}
- (IBAction)digitPressed:(UIButton *)sender {
NSString *digit = sender.currentTitle;
if ([digit isEqualToString:#"."]) {
if ([self.display.text rangeOfString:#"."].location != NSNotFound) {
return;
}
else {
if (!self.userIsInTheMiddleOfEnteringANumber) digit = #"0.";
}
}
[self updateStackDisplay:digit];
if (self.userIsInTheMiddleOfEnteringANumber) {
self.display.text = [self.display.text stringByAppendingString:digit];
}
else {
self.display.text = digit;
self.userIsInTheMiddleOfEnteringANumber = YES;
}
}
- (IBAction)operationPressed:(UIButton *)sender {
if (self.userIsInTheMiddleOfEnteringANumber) [self enterPressed];
[self updateStackDisplay:sender.currentTitle];
[self updateStackDisplay:#"="];
double result = [self.brain performOperation:sender.currentTitle];
NSString *resultString = [NSString stringWithFormat:#"%g", result];
self.display.text = resultString;
}
- (IBAction)enterPressed {
if (self.userIsInTheMiddleOfEnteringANumber) {
self.userIsInTheMiddleOfEnteringANumber = NO;
}
[self.brain pushOperand:[self.display.text doubleValue]];
}
- (IBAction)clearPressed {
self.stackDisplay.text = #"";
[self.brain clearStack];
self.userIsInTheMiddleOfEnteringANumber = NO;
self.display.text = #"0";
}
#end
I got a maybe similar error, where some UIButtons had empty text, although by outputting titleLabel.text got the correct text value. I deleted the Buttons and added them identically to what it has before been in the storyboard. Then it worked.

Updating NSView asynchronously from a thread

First of all, I'm an Objective-C novice. So I'm not very familiar with OS X or iOS development. My experience is mostly in Java.
I'm creating an agent-based modeling-framework. I'd like to display the simulations and to do that I'm writing a little application. First, a little bit about the framework. The framework has a World class, in which there is a start method, which iterates over all agents and has them perform their tasks. At the end of one "step" of the world (i.e., after all the agents have done their thing), the start method calls the intercept method of an object that implements InterceptorProtocol. This object was previously passed in via the constructor. Using the interceptor, anyone can get a hook into the state of the world. This is useful for logging, or in the scenario that I'm trying to accomplish: displaying the information in a graphical manner. The call to intercept is synchronous.
Now as far as the GUI app is concerned, it is pretty simple. I have a controller that initializes a custom view. This custom view also implements InterceptorProtocol so that it can listen in, to what happens in the world. I create a World object and pass in the view as an interceptor. The view maintains a reference to the world through a private property and so once I have initialized the world, I set the view's world property to the world I have just created (I realize that this creates a cycle, but I need a reference to the world in the drawRect method of the view and the only way I can have it is if I maintain a reference to it from the class).
Since the world's start method is synchronous, I don't start the world up immediately. In the drawRect method I check to see if the world is running. If it is not, I start it up in a background thread. If it is, I examine the world and display all the graphics that I need to.
In the intercept method (which gets called from start running on the background thread), I set setNeedsToDisplay to YES. Since the start method of the world is running in a separate thread, I also have a lock object that I use to synchronize so that I'm not working on the World object while it's being mutated (this part is kind of janky and it's probably not working the way I expect it to - there are more than a few rough spots and I'm simply trying to get a little bit working; I plan to clean up later).
My problem is that the view renders some stuff, and then it pretty much locks up. I can see that the NSLog statements are being called and so the code is running, but nothing is getting updated on the view.
Here's some of the pertinent code:
MasterViewController
#import "MasterViewController.h"
#import "World.h"
#import "InfectableBug.h"
#interface MasterViewController ()
#end
#implementation MasterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_worldView = [[WorldView alloc] init];
World* world = [[World alloc] initWithName: #"Bhumi"
rows: 100
columns: 100
iterations: 2000
snapshotInterval: 1
interceptor: _worldView];
for(int i = 0; i < 999; i++) {
NSMutableString* name = [NSMutableString stringWithString: #"HealthyBug"];
[name appendString: [[NSNumber numberWithInt: i] stringValue]];
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: name
layer: #"FirstLayer"
infected: NO
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
}
NSLog(#"Added all bugs. Going to add infected");
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: #"InfectedBug"
layer: #"FirstLayer"
infected: YES
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
[_worldView setWorld: world];
//[world start];
}
return self;
}
- (NSView*) view {
return self.worldView;
}
#end
WorldView
#import "WorldView.h"
#import "World.h"
#import "InfectableBug.h"
#implementation WorldView
#synthesize world;
- (id) initWithFrame:(NSRect) frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void) drawRect:(NSRect) dirtyRect {
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
CGContextClearRect(myContext, CGRectMake(0, 0, 1024, 768));
NSUInteger rows = [world rows];
NSUInteger columns = [world columns];
NSUInteger cellWidth = 1024 / columns;
NSUInteger cellHeight = 768 / rows;
if([world running]) {
#synchronized (_lock) {
//Ideally we would need layers, but for now let's just get this to display
NSArray* bugs = [world bugs];
NSEnumerator* enumerator = [bugs objectEnumerator];
InfectableBug* bug;
while ((bug = [enumerator nextObject])) {
if([bug infected] == YES) {
CGContextSetRGBFillColor(myContext, 128, 0, 0, 1);
} else {
CGContextSetRGBFillColor(myContext, 0, 0, 128, 1);
}
NSLog(#"Drawing bug %# at %lu, %lu with width %lu and height %lu", [bug name], [bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight);
CGContextFillRect(myContext, CGRectMake([bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight));
}
}
} else {
[world performSelectorInBackground: #selector(start) withObject: nil];
}
}
- (BOOL) isFlipped {
return YES;
}
- (void) intercept: (World *) aWorld {
struct timespec time;
time.tv_sec = 0;
time.tv_nsec = 500000000L;
//nanosleep(&time, NULL);
#synchronized (_lock) {
[self setNeedsDisplay: YES];
}
}
#end
start method in World.m:
- (void) start {
running = YES;
while(currentIteration < iterations) {
#autoreleasepool {
[bugs shuffle];
NSEnumerator* bugEnumerator = [bugs objectEnumerator];
Bug* bug;
while((bug = [bugEnumerator nextObject])) {
NSString* originalLayer = [bug layer];
NSUInteger originalX = [bug x];
NSUInteger originalY = [bug y];
//NSLog(#"Bug %# is going to act and location %i:%i is %#", [bug name], [bug x], [bug y], [self isOccupied: [bug layer] x: [bug x] y: [bug y]] ? #"occupied" : #"not occupied");
[bug act];
//NSLog(#"Bug has acted");
if(![originalLayer isEqualToString: [bug layer]] || originalX != [bug x] || originalY != [bug y]) {
//NSLog(#"Bug has moved");
[self moveBugFrom: originalLayer atX: originalX atY: originalY toLayer: [bug layer] atX: [bug x] atY: [bug y]];
//NSLog(#"Updated bug position");
}
}
if(currentIteration % snapshotInterval == 0) {
[interceptor intercept: self];
}
currentIteration++;
}
}
//NSLog(#"Done.");
}
Please let me know if you'd like to see any other code. I realize that the code is not pretty; I was just trying to get stuff to work and I plan on cleaning it up later. Also, if I'm violating an Objective-C best practices, please let me know!
Stepping out for a bit; sorry if I don't respond immediately!
Whew, quiet a question for probably a simple answer: ;)
UI updates have to be performed on the main thread
If I read your code correctly, you call the start method on a background thread. The start method contains stuff like moveBugFrom:... and also the intercept: method. The intercept method thus calls setNeedsDisplay: on a background thread.
Have all UI related stuff perform on the main thread. Your best bet is to use Grand Central Dispatch, unless you need to support iOS < 4 or OS X < 10.6 (or was it 10.7?), like this:
dispatch_async(dispatch_get_main_queue(), ^{
// perform UI updates
});

Xcode seems to not want to include my class

So, I have a class called MazeCell which is declared in "MazeCell.h"
#import <Foundation/Foundation.h>
enum {
MazeCellEdgeWall = 0,
MazeCellEdgeGate = 1,
MazeCellEdgeExit = 2
};
typedef NSUInteger MazeCellEdge;
#interface MazeCell : NSObject {
MazeCellEdge left;
MazeCellEdge right;
MazeCellEdge down;
MazeCellEdge up;
NSUInteger drawCount;
NSUInteger row;
NSUInteger column;
}
#property MazeCellEdge left;
#property MazeCellEdge right;
#property MazeCellEdge down;
#property MazeCellEdge up;
#property NSUInteger drawCount;
#property NSUInteger row;
#property NSUInteger column;
- (id)initWithLeft:(MazeCellEdge)newLeft
right:(MazeCellEdge)newRight
up:(MazeCellEdge)newUp
down:(MazeCellEdge)newDown
row:(NSUInteger)newRow
column:(NSUInteger)newColumn;
#end
Xcode keeps displaying warnings like "warning: 'MazeView' may not respond to '-left'" for all the methods. The funny thing is that the code runs fine on the simulator, it's just that XCode doesn't know the methods.
I was content to ignore the messages until XCode wouldn't let me use MazeCellEdgeWall because it hadn't been declared earlier (all these warnings and errors are in different classes).
So I was wondering if anyone saw any blatant errors that I may have missed because I'm new to programming in general.
Edit: I didn't originally include the code since it is long, but here is the code giving errors.
Here is "MazeCell.m":
#import "MazeCell.h"
#implementation MazeCell
#synthesize left;
#synthesize right;
#synthesize down;
#synthesize up;
#synthesize drawCount;
#synthesize row;
#synthesize column;
-(id) init {
if (self = [super init]) {
right = MazeCellEdgeWall;
up = MazeCellEdgeWall;
left = MazeCellEdgeWall;
down = MazeCellEdgeWall;
drawCount = 0;
}
return self;
}
- (id)initWithLeft:(MazeCellEdge)newLeft
right:(MazeCellEdge)newRight
up:(MazeCellEdge)newUp
down:(MazeCellEdge)newDown
row:(NSUInteger)newRow
column:(NSUInteger)newColumn
{
if (self = [super init]) {
left = newLeft;
right = newRight;
up = newUp;
down = newDown;
drawCount = 0;
row = newRow;
column = newColumn;
}
return self;
}
#end
Here is MazeView.h:
#import "MazeView.h"
#import "MazeCell.h"
#import "NSMutableArray+Stack.h"
#define kCellSidesSize 80.0
#implementation MazeView
#synthesize maze;
#synthesize controller;
#synthesize interfaceOrientation;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
interfaceOrientation = UIInterfaceOrientationPortrait;
[self setBackgroundColor:[UIColor greenColor]];
[self setUserInteractionEnabled:YES];
[self setMaze:[[Maze alloc] initWithSize:MazeSizeMake(4, 6)]];
}
return self;
}
- (void)setMaze:(Maze *)newMaze {
maze = newMaze;
CGRect newFrame = [self frame];
newFrame.size = CGSizeMake([newMaze size].width * kCellSidesSize,
[newMaze size].height * kCellSidesSize);
[self setFrame:newFrame];
}
- (void)setInterfaceOrientation:(UIInterfaceOrientation)newOrientation {
if (interfaceOrientation != newOrientation) {
interfaceOrientation = newOrientation;
CGRect oldFrame = [self frame];
[self setFrame:CGRectMake(oldFrame.origin.y, oldFrame.origin.x,
oldFrame.size.height, oldFrame.size.width)];
[[self superview] setContentSize:[self frame].size];
}
}
- (void)setController:(UIViewController *)newController {
if (controller != newController) {
controller = newController;
}
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
NSUInteger columns = [[self maze] size].width;
NSUInteger rows = [[self maze] size].height;
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(context, kCellSidesSize - 2.0);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
BOOL isDrawing = NO;
MazeCell *aCell;
NSMutableArray *aStack = [[NSMutableArray alloc] init];
NSUInteger row = 0;
NSUInteger column = 0;
while (YES) {
aCell = [maze getCellInRow:row andColumn:column ofOrientation:interfaceOrientation];
if (isDrawing) {
CGContextAddLineToPoint(context, row * kCellSidesSize + kCellSidesSize / 2.0,
column * kCellSidesSize + kCellSidesSize / 2.0);
} else {
isDrawing = YES;
CGContextMoveToPoint(context, row * kCellSidesSize + kCellSidesSize / 2.0,
column * kCellSidesSize + kCellSidesSize / 2.0);
}
if ([aCell left] == MazeCellEdgeExit && [aCell drawCount] < 1) {
//Warnings and errors
[aCell setDrawCount:1]; //Warning
column--;
} else if ([aCell right] == MazeCellEdgeExit && [aCell drawCount] < 2) {
//Warnings and errors
[aCell setDrawCount:2]; //Warning
column++;
} else if ([aCell up] == MazeCellEdgeExit && [aCell drawCount] < 3) {
//Warnings and errors
[aCell setDrawCount:3]; //Warning
row--;
} else if ([aCell down] == MazeCellEdgeExit && [aCell drawCount] < 4) {
//Warnings and errors
[aCell setDrawCount:4]; //Warning
row++;
} else if ([aStack count] > 0) {
aCell = [aStack pop];
row = [aCell row]; //Warning
column = [aCell column]; //Warning
isDrawing = NO;
} else {
break;
}
}
CGContextStrokePath(context);
[aStack release];
}
#end
Again, this is provided to prove that I have coded things. This program works and, as I said, the maze cell methods actually do work, it's just that Xcode is giving me warnings which I wouldn't care about, except that it says I haven't defined MazeCellEdgeExit and so doesn't compile anymore, but it does compile otherwise.
Now this is quite strange. But I have discovered that duplicating the MazeCell.h and MazeCell.m files and renaming them to MzCell.h and MzCell.m, and then replacing every reference to MazeCell with MzCell got this program to run.
Although that opens up more questions than it answers...
Everything looks good to me. Try a clean and build. It's quite possible that outdated object files are floating around, confusing the compiler or linker.
I haven't been able to detect a bug that would prevent compilation, although there are a number of apparent memory leaks, such as in setMaze: where you don't release the old maze. (You definitely allocate a maze in -initWithFrame: so you leak at least that one.) Also, the default setter semantic is "assign", not "retain" or "copy" — in this case, it would seem that one of the latter two behaviors would make more sense. I realize you're new to Objective-C, so these are meant by way of constructive feedback, not criticism. :-)
You neglect to include the code that's actually generating the warnings, but it sounds like wherever it is, you haven't imported the MazeCell.h header file.
It's possible that the object ('MazeView') you're alloc'ing and init'ing might not be a 'MazeCell' object. This may be caused by neglecting to import the MazeCell.h as Chuck mentioned or your simply creating the 'MazeView' object with the wrong class, maybe as a result of copy and pasting your own code (just a guess, as it seems counter-intuitive to create an object of a MazeCell type and name it 'MazeView').
I also suspect, though don't have access right now to Xcode to test, that your typedef might be declared incorrectly (or I'm simply not familiar with that syntax style). Try replacing your 6 lines of the typedef with this.
typedef enum {
MazeCellEdgeWall = 0,
MazeCellEdgeGate = 1,
MazeCellEdgeExit = 2
} MazeCellEdge;
EDIT:
Since you have just included the implementation file, I noticed that it's for MazeView (line 7), but your original header file is for MazeCell. So as of right now, you actually haven't written any code (or not posted any) that is actually for a MazeCell object.
I'm also suspecting that you haven't #synthesize'ed (or written methods for them) any of your instance variables.