BardiaD BardiaD - 3 months ago 9
iOS Question

C4Shape Custom Subclass init issues?

I've asked another question regarding this project before and Travis was super helpful. Previous question

Taking that advice into mind I'm trying to create a subclass for the C4Shape class, I added 2 properties (both floats) to the class for X and Y position values. The reason I'm not just calling the .center property of a C4Shape is because for adding them to the canvas I prefer to use the top left corner instead of the center.

I'm trying to write a custom Init method for this new class, however I'm getting an error.

Here is the custom init code I'm working with:

customShape.m

- (id)initWithColor:(UIColor *)fillColor atX:(float)_xValue atY:(float)_yValue
{
CGRect frame = CGRectMake(_xValue, _yValue, 100, 100);
self = [customShape rect:frame];

self.lineWidth = 0.0f;
self.fillColor = fillColor;
self.xValue = _xValue;
self.yValue = _yValue;


return self;
}


C4WorkSpace.m

-(void)setup {
customShape *testShape = [[customShape alloc]initWithColor:[UIColor greenColor] atX:50.0f atY:50.0f];

[self.canvas addShape:testShape];
}


I suspect the culprit is
self = [customShape rect:frame];
This is the warning I see: "Incompatible pointer type assigning to 'customeShape *_strong' from 'C4Shape *'"

The actual error that gets thrown when I try to run this is: "Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[C4Shape setXValue:]: unrecognized selector sent to instance 0x9812580'"

As before I'm making buttons that can hold a color value and when you tap that button it will send a UDP packet with that buttons fillColor as well as the iPads IP.

Answer

You're quite close with the implementation of your init method. I would restructure it in the following way:

- (id)initWithColor:(UIColor *)aColor origin:(CGPoint)aPoint {
    self = [super init];
    if(self != nil) {
        CGRect frame = CGRectMake(0,0, 100, 100);
        [self rect:frame];
        self.lineWidth = 0.0f;
        self.fillColor = aColor;
        self.origin = aPoint;
    }
    return self;
}

A couple things to note:

  1. When subclassing it's always good to call the init method of the object's superclass
  2. It's good practice to wrap the init of your subclass in an if statement, checking to see if the super class init returned properly.
  3. Create a frame for your new object and call rect: on self directly.
  4. There is an origin point in every visible C4 object, so instead of using x and y values directly, you can set the origin with a CGPoint (the origin is the top-left corner).

You then need to add this method to your .h file:

@interface MyShape : C4Shape
-(id)initWithColor:(UIColor *)aColor origin:(CGPoint)aPoint;
@end

Finally, you can create your shape in your C4WorkSpace like this:

MyShape *m = [[MyShape alloc] initWithColor:[UIColor darkGrayColor]
                                     origin:CGPointMake(100, 100)];

And, if you add a line to your tapped method you can check the origin point of the button:

-(void)heardTap:(NSNotification *)aNotification {
    MyShape *notificationShape = (MyShape *)[aNotification object];
    C4Log(@"%4.2f,%4.2f",notificationShape.center.x,notificationShape.center.y);
    C4Log(@"%4.2f,%4.2f",notificationShape.origin.x,notificationShape.origin.y);
    C4Log(@"%@",notificationShape.strokeColor);
}

While you can work with x and y values as properties, I recommend working with CGPoint structures. It's almost the same, except as you progress from C4 into Objective-C you'll notice that CGPoint and other CG geometry structures are used everywhere.

Comments