[SOLVED] Extending a native class (MKCircle)

In RoboVM I extended MKCircle with a custom class that allowed me to store stroke color and fill color variables for use in the MapView’s overlay renderer. I can’t figure out how to do the same in MOE.

I’ve successfully extended a native class before but MKCircle is tricky because the only way to set the radius of the circle is through a static init function. This function returns an MKCircle but I need to be able to set the radius of my class CustomCircle. I’ve tried several combinations of inits and Java constructors but can’t seem to initialize an actual CustomCircle object that is recognized by the Java instanceof operator.

Seems like what I have currently should work but it’s not recognized as a CustomCircle object in the MapViewDelegate’s mapViewRendererForOverlay(MKMapView mapView, @Mapped(ObjCObjectMapper.class) Object overlay) method, so I’m clearly doing something wrong. Here’s my CustomCircle class:

import org.moe.natj.general.Pointer;
import org.moe.natj.general.ann.Owned;
import org.moe.natj.objc.ann.Selector;

import apple.corelocation.struct.CLLocationCoordinate2D;
import apple.mapkit.MKCircle;
import apple.uikit.UIColor;

public class CustomCircle extends  MKCircle {

UIColor strokeColor = UIColor.blackColor();
UIColor fillColor = UIColor.clearColor();
double lineWidth = 1;
MKCircle circle;

protected CustomCircle(Pointer peer) {
	super(peer);
}

@Owned
@Selector("alloc")
public static native CustomCircle alloc();

public CustomCircle init() {
	super.init();
	return this;
}

public CustomCircle initWithCenterCoordinateRadius(CLLocationCoordinate2D coord, double radius) {
	init();
	circle = MKCircle.circleWithCenterCoordinateRadius(coord, radius);
	return this;
}
}

With the above code I’m trying to make a class that inherits from MKCircle so I can pass it as a circle. Then I use MKCircle’s static init method to generate a circle with the coordinate and radius that I need, and I store a reference to that. That is the actual circle that I end up using. It’s really convoluted but I can’t figure out any way to make it work.

With RoboVM I just had a simple constructor for CustomCircle that initialized with the coordinate and radius. I looked behind the scenes of their constructor system and it looked like they used the static init method of MKCircle and passed a handle to that object in the constructor call. I got lost in that so couldn’t figure it out, but it seems to resemble the default Pointer peer constructor that the native classes have. Maybe that’s how I could achieve it?

Any help is appreciated, thanks!

1 Like

After playing around with it for a few more hours I finally found a working solution. It’s very convoluted because the CustomCircle class is actually just a holder for the actual MKCircle. The custom class extends MKShape and implements MKOverlay so it can be added to the map. Then when the delegate processes it, I use the actual MKCircle that’s stored in the custom class, and then I can color it and such with the other options in the custom class.

Here’s the final CustomCircle class in case anyone cares

@Runtime(ObjCRuntime.class)
@ObjCClassName("CustomCircle")
@RegisterOnStartup
public class CustomCircle extends MKShape implements MKOverlay {
	UIColor strokeColor = UIColor.blackColor();
	UIColor fillColor = UIColor.clearColor();
	double lineWidth = 1;
	MKCircle circle;

protected CustomCircle(Pointer peer) {
	super(peer);
}

@Owned
@Selector("alloc")
public static native CustomCircle alloc();

@Owned
@Selector("init")
public native CustomCircle init();

public CustomCircle initWithCenterCoordinateRadius(CLLocationCoordinate2D coord, double radius) {
	init();
	circle = MKCircle.circleWithCenterCoordinateRadius(coord, radius);
	return this;
}

@Override
public MKMapRect boundingMapRect() {
	return circle.boundingMapRect();
    }
}

I should point out that the key to this solution was to both make it appear as a legit MKOverlay (so it could be passed to the map delegate) and to give it the same coordinate and radius as the actual circle, so it will actually be passed to the delegate when the actual circle is supposed to be on screen. Returning the boundingMapRect of the actual circle solved this.

1 Like

Dear MrPat!

Please try it:

@Selector("init")
public native CustomCircle init();

public CustomCircle initWithCenterCoordinateRadius(CLLocationCoordinate2D coord, double radius) {
    CustomCircle customCircle = init();
    customCircle.circle = MKCircle.circleWithCenterCoordinateRadius(coord, radius);
    return customCircle;
}

Best Regards,
Roland