Convert Objective C to Monotouch (Rounded Table Grouped) - xamarin

I have the following Objective C code:
CGFloat cornerRadius = 5.f;
cell.backgroundColor = UIColor.clearColor;
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
CGMutablePathRef pathRef = CGPathCreateMutable();
CGRect bounds = CGRectInset(cell.bounds, 10, 0);
BOOL addLine = NO;
if (indexPath.row == 0 && indexPath.row == [tableView numberOfRowsInSection:indexPath.section]-1) {
CGPathAddRoundedRect(pathRef, nil, bounds, cornerRadius, cornerRadius);
} else if (indexPath.row == 0) {
CGPathMoveToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMaxY(bounds));
CGPathAddArcToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMinY(bounds), CGRectGetMidX(bounds), CGRectGetMinY(bounds), cornerRadius);
CGPathAddArcToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMinY(bounds), CGRectGetMaxX(bounds), CGRectGetMidY(bounds), cornerRadius);
CGPathAddLineToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMaxY(bounds));
addLine = YES;
} else if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section]-1) {
CGPathMoveToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
CGPathAddArcToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMaxY(bounds), CGRectGetMidX(bounds), CGRectGetMaxY(bounds), cornerRadius);
CGPathAddArcToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMaxY(bounds), CGRectGetMaxX(bounds), CGRectGetMidY(bounds), cornerRadius);
CGPathAddLineToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMinY(bounds));
} else {
CGPathAddRect(pathRef, nil, bounds);
addLine = YES;
}
layer.path = pathRef;
CFRelease(pathRef);
layer.fillColor = [UIColor colorWithWhite:1.f alpha:0.8f].CGColor;
if (addLine == YES) {
CALayer *lineLayer = [[CALayer alloc] init];
CGFloat lineHeight = (1.f / [UIScreen mainScreen].scale);
lineLayer.frame = CGRectMake(CGRectGetMinX(bounds)+10, bounds.size.height-lineHeight, bounds.size.width-10, lineHeight);
lineLayer.backgroundColor = tableView.separatorColor.CGColor;
[layer addSublayer:lineLayer];
}
UIView *testView = [[UIView alloc] initWithFrame:bounds];
[testView.layer insertSublayer:layer atIndex:0];
testView.backgroundColor = UIColor.clearColor;
cell.backgroundView = testView;
That I translate into C# (Xamarin):
float cornerRadius = 5f;
cell.BackgroundColor = UIColor.Clear;
CAShapeLayer layer = new CAShapeLayer ();
CGPath pathRef = new CGPath();
RectangleF bounds = new RectangleF(cell.Bounds.X,cell.Bounds.Y, 5, 0);
bool addLine = false;
if (indexPath.Row == 0 && indexPath.Row == tableView.NumberOfRowsInSection(indexPath.Section)-1) {
pathRef.AddRoundedRect (bounds, cornerRadius, cornerRadius);
} else if (indexPath.Row == 0) {
pathRef.MoveToPoint (bounds.GetMinX(), bounds.GetMaxY());
pathRef.AddArcToPoint(bounds.GetMinX(),bounds.GetMinY(), bounds.GetMidX(),bounds.GetMinY(),cornerRadius);
pathRef.AddArcToPoint(bounds.GetMaxX(),bounds.GetMinY(), bounds.GetMaxX(),bounds.GetMidY(),cornerRadius);
pathRef.AddLineToPoint(bounds.GetMaxX(),bounds.GetMaxY());
addLine = true;
} else if (indexPath.Row == tableView.NumberOfRowsInSection(indexPath.Section)-1) {
pathRef.MoveToPoint (bounds.GetMinX(), bounds.GetMinY());
pathRef.AddArcToPoint(bounds.GetMinX(),bounds.GetMaxY(), bounds.GetMidX(),bounds.GetMaxY(),cornerRadius);
pathRef.AddArcToPoint(bounds.GetMaxX(),bounds.GetMaxY(), bounds.GetMaxX(),bounds.GetMidY(),cornerRadius);
pathRef.AddLineToPoint(bounds.GetMaxX(),bounds.GetMinY());
} else {
pathRef.AddRect (bounds);
addLine = true;
}
layer.Path = pathRef;
//CFRelease(pathRef);
layer.FillColor = new CGColor (1f, 0.8f);// UIColor.FromWhiteAlpha (1f, 0.8f);
if (addLine) {
CALayer lineLayer = new CALayer ();
float lineHeight = (1f / UIScreen.MainScreen.Scale);
lineLayer.Frame = new RectangleF (bounds.GetMinX () + 10, bounds.Size.Height - lineHeight, bounds.Size.Width - 10, lineHeight);
lineLayer.BackgroundColor = tableView.SeparatorColor.CGColor;
layer.AddSublayer (lineLayer);
}
UIView testView = new UIView (bounds);
testView.Layer.InsertSublayer (layer, 0);
testView.BackgroundColor = UIColor.Clear;
cell.BackgroundView = testView;
But it's now working, I don't get an app crash neither an error when I build.
I want to achieve the following from this question:
iOS 7 TableView like in Settings App on iPad

Related

core-plot x-axis label not visible - bounding rects?

Using the vertical bar chart sample as a start, I created a chart whose y values are 0..1, x values 1..n but the x label index supplied for CPTBarPlotFieldBarLocation cell of my data source doesn't appear; I don't use y-axis labels.
I suspect I'm clipping them. Is there a way, like in the old IB, to draw the various graph region's bounding rect?
The x-axis plot labels are a non-numeric string as in the example. What did I break? Here's my setup
//
// InfoBarView.m
#import "InfoBarView.h"
// Item index to color mapping for legend symbols not item related
static NSUInteger fldMapping[] = { 5, 6, 7, 0 };
static const BOOL kUseHorizontalBars = NO;
static NSArray <__kindof NSString *> const* kTargets = nil;
static NSArray <__kindof NSString *> const* kGetters = nil;
#interface BarChart()
#property (nonatomic, readwrite, strong, nullable) CPTPlotSpaceAnnotation * symbolTextAnnotation;
#end
#implementation BarChart
#synthesize symbolTextAnnotation;
#synthesize plotData;
+ (void)initialize
{
kTargets = [#[ #"grpA" , #"grpB", #"grpC", #"grpD" ] retain];
kGetters = [#[ #"fldA", #"fldB", #"fldC", #"fldD" ] retain];
}
- (void)awakeFromNib
{
// Get our orig scaling
[super awakeFromNib];
}
- (void)killGraph
{
if ( self.graphs.count )
{
CPTGraph * graph = self.defaultGraph;
CPTPlotSpaceAnnotation *annotation = self.symbolTextAnnotation;
if ( annotation )
{
[graph.plotAreaFrame.plotArea removeAnnotation:annotation];
self.symbolTextAnnotation = nil;
}
}
[super killGraph];
}
- (void)generateData
{
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
NSUInteger rndx = controller.barDataRanking;
NSArray * rankings = #[ #"Rank", #"Runs" ];
GroupItem * group = controller.group;
CPTGraph * graph = [self defaultGraph];
CPTPlotSpaceAnnotation * annotation = self.symbolTextAnnotation;
if ( annotation )
{
if (graph)
{
[graph.plotAreaFrame.plotArea removeAnnotation:annotation];
self.symbolTextAnnotation = nil;
}
}
// Update X title with new controller index
if (graph)
{
graph.title = [NSString stringWithFormat:#"Past Performances by %#", rankings[rndx]];
}
// Figure out our rank selector
NSString * rankSelectorName = [NSString stringWithFormat:#"%#%#Compare:",
kTargets[controller.barDataIndex],
rankings[rndx]];
SEL dataRanking = NSSelectorFromString(rankSelectorName);
self.plotData = (id)[group.items sortedArrayUsingSelector:dataRanking];
}
- (void)renderInGraphHostingView:(nonnull CPTGraphHostingView *)hostingView
withTheme:(nullable CPTTheme *)theme
animated:(BOOL)animated
{
#if TARGET_OS_SIMULATOR || TARGET_OS_IPHONE
CGRect bounds = hostingView.bounds;
#else
CGRect bounds = NSRectToCGRect(hostingView.bounds);
#endif
InfoController * controller = ((InfoBarView *)hostingView.superview).controller;
CPTGraph * graph = [[[CPTXYGraph alloc] initWithFrame:bounds] autorelease];
NSString * titles = #"fldA fldB fldC fldD";
GroupItem * group = controller.group;
CPTBarPlot * barPlot;
// Initially we default by rank
self.title = #"Past Performances by Rank";
[self addGraph:graph toHostingView:hostingView];
graph.plotAreaFrame.masksToBorder = NO;
if ( kUseHorizontalBars )
{
graph.plotAreaFrame.paddingBottom += self.titleSize;
}
else
{
graph.plotAreaFrame.paddingLeft += self.titleSize;
}
// Add plot space for bar charts
CPTXYPlotSpace * barPlotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;
[barPlotSpace setScaleType:CPTScaleTypeCategory forCoordinate:CPTCoordinateX];
if ( kUseHorizontalBars )
{
barPlotSpace.xRange = [CPTPlotRange plotRangeWithLocation:#(-10.0) length:#120.0];
barPlotSpace.yRange = [CPTPlotRange plotRangeWithLocation:#(-1.0) length:#11.0];
}
else
{
barPlotSpace.xRange = [CPTPlotRange plotRangeWithLocation:#(-0.5) length:#(group.itemCount)];
barPlotSpace.yRange = [CPTPlotRange plotRangeWithLocation:#(-0.05) length:#1.0];
}
barPlotSpace.allowsUserInteraction = YES;
[graph addPlotSpace:barPlotSpace];
barPlotSpace.delegate = self;
// Create grid line styles
CPTMutableLineStyle * majorGridLineStyle = [CPTMutableLineStyle lineStyle];
majorGridLineStyle.lineWidth = 1.0;
majorGridLineStyle.lineColor = [[CPTColor grayColor] colorWithAlphaComponent:0.75];
CPTMutableLineStyle * minorGridLineStyle = [CPTMutableLineStyle lineStyle];
minorGridLineStyle.lineWidth = 1.0;
minorGridLineStyle.lineColor = [[CPTColor whiteColor] colorWithAlphaComponent:0.25];
// Create axes
CPTXYAxisSet * axisSet = (CPTXYAxisSet *)graph.axisSet;
CPTXYAxis * x = axisSet.xAxis;
{
x.majorIntervalLength = (kUseHorizontalBars ? #10.0 : #1.0);
x.minorTicksPerInterval = (kUseHorizontalBars ? 9 : 0);
x.orthogonalPosition = (kUseHorizontalBars ? #(-0.5) : #0.0);
x.majorGridLineStyle = majorGridLineStyle;
x.minorGridLineStyle = minorGridLineStyle;
x.axisLineStyle = nil;
x.majorTickLineStyle = nil;
x.minorTickLineStyle = nil;
x.labelOffset = self.titleSize * CPTFloat(2.5);
if ( kUseHorizontalBars )
{
x.visibleRange = [CPTPlotRange plotRangeWithLocation:#0.0 length:#10.0];
x.gridLinesRange = [CPTPlotRange plotRangeWithLocation:#(-0.5) length:#(group.itemCount)];
}
else
{
x.visibleRange = [CPTPlotRange plotRangeWithLocation:#(-0.5) length:#(group.itemCount)];
x.gridLinesRange = [CPTPlotRange plotRangeWithLocation:#0.0 length:#(1.0)];
}
x.plotSpace = barPlotSpace;
}
CPTXYAxis * y = axisSet.yAxis;
{
y.majorIntervalLength = (kUseHorizontalBars ? #1.0 : #10.0);
y.minorTicksPerInterval = (kUseHorizontalBars ? 0 : 9);
y.orthogonalPosition = (kUseHorizontalBars ? #0.0 : #(-0.5) );
y.preferredNumberOfMajorTicks = 8;
y.majorGridLineStyle = majorGridLineStyle;
y.minorGridLineStyle = minorGridLineStyle;
y.axisLineStyle = nil;
y.majorTickLineStyle = nil;
y.minorTickLineStyle = nil;
y.labelOffset = self.titleSize * CPTFloat(0.175);
y.titleLocation = (kUseHorizontalBars ? #55.0 : #(group.itemCount-1));
if ( kUseHorizontalBars )
{
y.visibleRange = [CPTPlotRange plotRangeWithLocation:#(-0.5) length:#10.0];
y.gridLinesRange = [CPTPlotRange plotRangeWithLocation:#0.0 length:#100.0];
}
else
{
y.visibleRange = [CPTPlotRange plotRangeWithLocation:#(-0.5) length:#(group.itemCount)];
y.gridLinesRange = [CPTPlotRange plotRangeWithLocation:#(-0.5) length:#(group.itemCount)];
}
y.title = titles;
y.titleOffset = self.titleSize * CPTFloat(0.25);
[barPlotSpace addCategory:title[seg] forCoordinate:CPTCoordinateY];
y.plotSpace = barPlotSpace;
}
// Set axes
graph.axisSet.axes = #[x, y];
// Create a shared bar line style
CPTMutableLineStyle * barLineStyle = [[[CPTMutableLineStyle alloc] init] autorelease];
barLineStyle.lineWidth = 1.0;
barLineStyle.lineColor = [CPTColor whiteColor];
// Marshall Y axis into individual plot identifiers
NSArray * title = [titles componentsSeparatedByString:#" "];
CPTMutableTextStyle * blackTextStyle = [CPTMutableTextStyle textStyle];
blackTextStyle.color = [CPTColor blackColor];
// Create bar plot(s)
for (int seg=0; seg<title.count; seg++)
{
barPlot = [[[CPTBarPlot alloc] init] autorelease];
barPlot.lineStyle = barLineStyle;
barPlot.fill = [CPTFill fillWithColor:[CPTColor colorAtIndex:seg]];
barPlot.barBasesVary = YES;
barPlot.barCornerRadius = 4.0;
barPlot.barWidth = #0.5;
barPlot.barsAreHorizontal = kUseHorizontalBars;
barPlot.dataSource = self;
barPlot.delegate = self;
barPlot.identifier = [NSString stringWithFormat:#"%#,%d",title[seg],seg],
barPlot.labelTextStyle = blackTextStyle;
barPlot.labelOffset = 0.0;
[graph addPlot:barPlot toPlotSpace:barPlotSpace];
NSLog(#"render:%#", barPlot.identifier);
}
// Add X category per slot
for (Item * item in plotData)
{
[barPlotSpace addCategory:item.numb forCoordinate:CPTCoordinateX];
}
}
#pragma mark -
#pragma mark Plot Delegate
- (void)Plot:(nonnull CPTPlot *)plot dataLabelWasSelectedAtRecordIndex:(NSUInteger)index
{
// NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:#","] lastObject];
NSLog(#"Data label for '%#' was selected at record %d.", plot.identifier, (int)index);
}
- (CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index;
{
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
CPTMutableTextStyle * textStyle = [CPTMutableTextStyle textStyle];
textStyle.color = [CPTColor colorAtIndex:item.foreRGB];
textStyle.textAlignment = CPTTextAlignmentCenter;
textStyle.fontSize = 24;
plot.labelOffset = 0;
CPTMutableShadow * shadow = [CPTMutableShadow shadow];
shadow.shadowColor = [CPTColor blackColor];//colorAtIndex:item.backRGB];
shadow.shadowOffset = CGSizeMake(1,-1);
shadow.shadowBlurRadius = 2.0f;
plot.labelShadow = shadow;
CPTTextLayer * textLayer = [[CPTTextLayer alloc] initWithText:item.numb.squish style:textStyle];
return [textLayer autorelease];
}
- (void)barPlot:(CPTBarPlot *)plot barWasSelectedAtRecordIndex:(NSUInteger)index
{
CGFloat floor = [[self valueForPlot:plot field:CPTBarPlotFieldBarBase recordIndex:index] doubleValue];
CGFloat value = [[self valueForPlot:plot field:CPTBarPlotFieldBarTip recordIndex:index] doubleValue];
CGFloat runs = 0;
NSString * barID = (NSString *)[[(id)plot.identifier
componentsSeparatedByString:#","] lastObject];
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int fndx = [barID intValue];
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
SEL dataItem = NSSelectorFromString(kTargets[controller.barDataIndex]);
DataItem * data = [item performSelector:dataItem];
runs = [[data fldD] doubleValue];
NSLog(#"BarSelected %# %# record:%lu. base:%.2f value:%.2f",
plot.identifier, data.name, index, floor, value);
CPTGraph * graph = [self defaultGraph];
CPTPlotSpaceAnnotation * annotation = self.symbolTextAnnotation;
if ( annotation )
{
[graph.plotAreaFrame.plotArea removeAnnotation:annotation];
self.symbolTextAnnotation = nil;
}
// Setup a style for the annotation
CPTMutableTextStyle * hitAnnotationTextStyle = [CPTMutableTextStyle textStyle];
hitAnnotationTextStyle.color = [CPTColor colorAtIndex:fldMapping[fndx]];
hitAnnotationTextStyle.fontSize = 16.0;
hitAnnotationTextStyle.fontName = #"Helvetica-Bold";
// Determine point of symbol in plot coordinates
NSNumber * x = #(index);
NSNumber * y = #(0.03 + (floor / runs));
CPTNumberArray * anchorPoint = (kUseHorizontalBars ? #[y, x] : #[x, y]);
// Add annotation
// First make a string for the y value
NSString * yString = [[NSNumberFormatter withFormat:#"#,###"] stringFromNumber:#(value)];
// Now add the annotation to the plot area
CPTPlotSpace * space = plot.plotSpace;
if ( space )
{
CPTTextLayer * textLayer = [[CPTTextLayer alloc] initWithText:yString style:hitAnnotationTextStyle];
annotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:space anchorPlotPoint:anchorPoint];
annotation.contentLayer = [textLayer autorelease];
annotation.displacement = CGPointMake(0.0, 0.0);
self.symbolTextAnnotation = annotation;
[graph.plotAreaFrame.plotArea addAnnotation:[annotation autorelease]];
}
}
#pragma mark Plot DataSource
- (NSUInteger)numberOfRecordsForPlot:(nonnull CPTPlot *)plot
{
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
return group.itemCount;
}
- (nullable id)numberForPlot:(nonnull CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:#","] lastObject];
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int fndx = [barID intValue];
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
SEL dataItem = NSSelectorFromString(kTargets[controller.barDataIndex]);
NSString * getName = kGetters[fndx];
SEL getter = NSSelectorFromString(getName);
SEL fldD = #selector(fldD);
CGFloat runs, base=0, value=0;
DataItem * data;
id num=nil;
// First get which data data item we're after
data = [item performSelector:dataItem];
runs = [[data performSelector:fldD] floatValue];
// Now fetch its getter
switch ( fieldEnum )
{
case CPTBarPlotFieldBarLocation:
num = item.numb;
break;
case CPTBarPlotFieldBarTip:
// Build base for this tip
for (int seg=0; seg < fndx; seg++)
{
value += [[data performSelector:NSSelectorFromString(kGetters[seg])] floatValue];
}
// Add this 'tip' value unless it's the total
if (fndx == 3)
num = #(1.0f);
else
{
value += [[data performSelector:getter] floatValue];
num = #(value/runs);
}
break;
case CPTBarPlotFieldBarBase:
// Calc base by sum prior fields
for (int seg=0; seg < fndx; seg++)
{
base += [[data performSelector:NSSelectorFromString(kGetters[seg])] floatValue];
}
num = #(base/runs);
}
return num;
}
- (nullable id)valueForPlot:(nonnull CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:#","] lastObject];
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int fndx = [barID intValue];
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
SEL dataItem = NSSelectorFromString(kTargets[controller.barDataIndex]);
NSString * getName = kGetters[fndx];
SEL getter = NSSelectorFromString(getName);
SEL fldD = #selector(fldD);
CGFloat runs, value=0;
DataItem * data;
id num=nil;
// First get which data block we're after
data = [item performSelector:dataItem];
runs = [[data performSelector:fldD] floatValue];
// Now fetch its getter
switch ( fieldEnum )
{
case CPTBarPlotFieldBarLocation:
num = item.numb.squish;
break;
case CPTBarPlotFieldBarTip:
value = [[data performSelector:getter] floatValue];
num = #(value);
break;
case CPTBarPlotFieldBarBase:
// Calc base by sum prior fields
for (int seg=0; seg < fndx; seg++)
{
value += [[data performSelector:NSSelectorFromString(kGetters[seg])] floatValue];
}
num = #(value);
}
return num;
}
- (nullable NSString *)legendTitleForBarPlot:(nonnull CPTBarPlot *)plot recordIndex:(NSUInteger)index
{
NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:#","] lastObject];
int fndx = [barID intValue];
return kGetters[fndx];
}
#pragma mark Plot Legend Delegate
- (void)legend:(CPTLegend *)legend legendEntryForPlot:(CPTPlot *)plot wasSelectedAtIndex:(NSUInteger)index
{
NSLog(#"legend:%# index:%lu", plot.identifier, index);
}
#pragma mark Plot Space Delegate
#pragma mark Plot Space Delegate
- (CPTPlotRange *)plotSpace:(CPTPlotSpace *)space
willChangePlotRangeTo:(CPTPlotRange *)newRange
forCoordinate:(CPTCoordinate)coordinate
{
CGFloat newLocation = newRange.location.floatValue;
// CGFloat newLength = newRange.length.floatValue;
// Range change to +2 point on data boundary
if (CPTCoordinateX == coordinate)
{
if (newLocation < -0.5)
{
return [CPTPlotRange plotRangeWithLocation:#(-0.5) length:newRange.length];
}
else
if (newLocation > (0.3))
{
return [CPTPlotRange plotRangeWithLocation:#(0.3) length:newRange.length];
}
}
if (CPTCoordinateY == coordinate)
{
if (newLocation < (-0.1))
{
return [CPTPlotRange plotRangeWithLocation:#(-0.1) length:newRange.length];
}
else
if (newLocation > (0.1))
{
return [CPTPlotRange plotRangeWithLocation:#(0.1) length:newRange.length];
}
}
// CGRect chartBounds = self.defaultGraph.bounds;
// NSLog(#"old:%#", NSStringFromRect(chartBounds));
// NSLog(#"new:%# %#", (coordinate == CPTCoordinateX ? #"X" : #"Y"),[newRange description]);
return newRange;
}
#end
#implementation InfoBarView
#synthesize barChart;
#synthesize x_title;
- (void)awakeFromNib
{
// Get our orig scaling
[super awakeFromNib];
self.barChart = [[[BarChart alloc] init] autorelease];
}
- (void)drawRect:(NSRect)clip
{
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
NSImage * webImage = controller.group.track.webImage;
GroupItem * group = controller.group;
NSRect rect = [self frame];
// Save state so we can restore later
CGContextSaveGState(context);
// Draw track image as our view's background
if (webImage)
{
[webImage drawInRect:rect fromRect:[webImage alignmentRect]
operation:NSCompositeSourceOver fraction:0.25];
}
if (group && !barChart.graphs.count)
{
[barChart renderInView:self withTheme:nil animated:YES];
}
CGContextRestoreGState(context);
}
#end
If there were visible clues - drawn bounding rects, I think a lot of us could benefit from them (I miss those from IB circa v3).
The statement x.labelFormatter = nil; eliminates the automatic axis labels. If you supply a label formatter and label text style, the axis will create ticks and labels automatically based on the labeling policy.

How to get an array of AXMenuItems from AXMenu?

For my code I am attempting to get an array of AXMenuItems from an AXMenu (AXUIElementRef). The menu logs successfully, and here is my code:
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
CFTypeRef aChildren;
AXUIElementCopyAttributeValue(anAXDockApp, kAXChildrenAttribute, &aChildren);
SafeCFRelease(anAXDockApp);
CFTypeRef aMenu = CFArrayGetValueAtIndex(aChildren, 0);
NSLog(#"aMenu: %#", aMenu);
// Get menu items
CFTypeRef aMenuChildren;
AXUIElementCopyAttributeValue(aMenu, kAXVisibleChildrenAttribute, &aMenuChildren);
for (NSInteger i = 0; i < CFArrayGetCount(aMenuChildren); i++) {
AXUIElementRef aMenuItem = [self copyAXUIElementFrom:aMenu role:kAXMenuItemRole atIndex:i];
NSLog(#"aMenuItem: %#", aMenuItem); // logs (null)
CFTypeRef aTitle;
AXUIElementCopyAttributeValue(aMenuItem, kAXTitleAttribute, &aTitle);
if ([(__bridge NSString *)aTitle isEqualToString:#"New Window"] || [(__bridge NSString *)aTitle isEqualToString:#"New Finder Window"]) /* Crashes here (i can see why)*/{
AXUIElementPerformAction(aMenuItem, kAXPressAction);
[NSThread sleepForTimeInterval:1];
break;
}
}
What is the correct way to get the list of AXMenuItems?
Screenshot of the Accessibility Inspector:
I have figured out an answer, using #Willeke answer of using AXUIElementCopyElementAtPosition() to get the menu. Since there were multiple dock orientations and hiding, I had to create enums in the .h file as it would be easier to read than 0, 1, or 2.
// .h
typedef enum {
kDockPositionBottom,
kDockPositionLeft,
kDockPositionRight,
kDockPositionUnknown
} DockPosition;
typedef enum {
kDockAutohideOn,
kDockAutohideOff
} DockAutoHideState;
Then, I added the methods to get these states in the .m
// .m
- (DockPosition)dockPosition
{
NSRect screenRect = [[NSScreen mainScreen] frame];
NSRect visibleRect = [[NSScreen mainScreen] visibleFrame];
// Dont need to remove menubar height
visibleRect.origin.y = 0;
if (visibleRect.origin.x > screenRect.origin.x) {
return kDockPositionLeft;
} else if (visibleRect.size.width < screenRect.size.width) {
return kDockPositionRight;
} else if (visibleRect.size.height < screenRect.size.height) {
return kDockPositionBottom;
}
return kDockPositionUnknown;
}
- (DockAutoHideState)dockHidden
{
NSString *plistPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Library/Preferences/com.apple.dock.plist"];
NSDictionary *dockDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
CFBooleanRef autohide = CFDictionaryGetValue((__bridge CFDictionaryRef)dockDict, #"autohide");
if (CFBooleanGetValue(autohide) == true) {
return kDockAutohideOn;
}
return kDockAutohideOff;
}
For the dock position unknown, I added in case it was not able to calculate it from the screen positions.
Then, I used a method to get the dock item from the menubar:
- (AXUIElementRef)getDockItemWithName:(NSString *)name
{
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
AXUIElementRef aList = [self copyAXUIElementFrom:anAXDockApp role:kAXListRole atIndex:0];
CFTypeRef aChildren;
AXUIElementCopyAttributeValue(aList, kAXChildrenAttribute, &aChildren);
NSInteger itemIndex = -1;
for (NSInteger i = 0; i < CFArrayGetCount(aChildren); i++) {
AXUIElementRef anElement = CFArrayGetValueAtIndex(aChildren, i);
CFTypeRef aResult;
AXUIElementCopyAttributeValue(anElement, kAXTitleAttribute, &aResult);
if ([(__bridge NSString *)aResult isEqualToString:name]) {
itemIndex = i;
}
}
SafeCFRelease(aChildren);
if (itemIndex == -1) return nil;
// We have index now do something with it
AXUIElementRef aReturnItem = [self copyAXUIElementFrom:aList role:kAXDockItemRole atIndex:itemIndex];
SafeCFRelease(aList);
return aReturnItem;
}
This SafeCFRelease() method is a very simple method that checks if the passed value is not nil, then releases (had some issues earlier).
void SafeCFRelease( CFTypeRef cf )
{
if (cf) CFRelease(cf);
}
And this method [copyAXUIElementFrom: role: atIndex:] is a method from #Willeke answer from another one of my questions:
- (AXUIElementRef)copyAXUIElementFrom:(AXUIElementRef)theContainer role:(CFStringRef)theRole atIndex:(NSInteger)theIndex {
AXUIElementRef aResultElement = NULL;
CFTypeRef aChildren;
AXError anAXError = AXUIElementCopyAttributeValue(theContainer, kAXChildrenAttribute, &aChildren);
if (anAXError == kAXErrorSuccess) {
NSUInteger anIndex = -1;
for (id anElement in (__bridge NSArray *)aChildren) {
if (theRole) {
CFTypeRef aRole;
anAXError = AXUIElementCopyAttributeValue((__bridge AXUIElementRef)anElement, kAXRoleAttribute, &aRole);
if (anAXError == kAXErrorSuccess) {
if (CFStringCompare(aRole, theRole, 0) == kCFCompareEqualTo)
anIndex++;
SafeCFRelease(aRole);
}
}
else
anIndex++;
if (anIndex == theIndex) {
aResultElement = (AXUIElementRef)CFRetain((__bridge CFTypeRef)(anElement));
break;
}
}
SafeCFRelease(aChildren);
}
return aResultElement;
}
Taking all this code, I put it into one of my methods:
// Check if in dock (otherwise cant do it)
if ([self isAppOfNameInDock:[appDict objectForKey:#"AppName"]]) {
// Get dock item
AXUIElementRef aDockItem = [self getDockItemWithName:[appDict objectForKey:#"AppName"]];
AXUIElementPerformAction(aDockItem, kAXShowMenuAction);
[NSThread sleepForTimeInterval:0.5];
CGRect aRect;
CFTypeRef aPosition;
AXUIElementCopyAttributeValue(aDockItem, kAXPositionAttribute, &aPosition);
AXValueGetValue(aPosition, kAXValueCGPointType, &aRect.origin);
SafeCFRelease(aPosition);
CFTypeRef aSize;
AXUIElementCopyAttributeValue(aDockItem, kAXSizeAttribute, &aSize);
AXValueGetValue(aSize, kAXValueCGSizeType, &aRect.size);
SafeCFRelease(aSize);
SafeCFRelease(aDockItem);
CGPoint aMenuPoint;
if ([self dockHidden] == kDockAutohideOff) {
switch ([self dockPosition]) {
case kDockPositionRight:
aMenuPoint = CGPointMake(aRect.origin.x - 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionLeft:
aMenuPoint = CGPointMake(aRect.origin.x + aRect.size.width + 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionBottom:
aMenuPoint = CGPointMake(aRect.origin.x + (aRect.size.width / 2), aRect.origin.y - 18);
break;
case kDockPositionUnknown:
aMenuPoint = CGPointMake(0, 0);
break;
}
} else {
NSRect screenFrame = [[NSScreen mainScreen] frame];
switch ([self dockPosition]) {
case kDockPositionRight:
aMenuPoint = CGPointMake(screenFrame.size.width - 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionLeft:
aMenuPoint = CGPointMake(screenFrame.origin.x + 18, aRect.origin.y + (aRect.size.height / 2));
break;
case kDockPositionBottom:
aMenuPoint = CGPointMake(aRect.origin.x + (aRect.size.width / 2), screenFrame.size.height - 18);
break;
case kDockPositionUnknown:
aMenuPoint = CGPointMake(0, 0);
break;
}
}
if ((aMenuPoint.x != 0) && (aMenuPoint.y != 0)) {
AXUIElementRef _systemWideElement = AXUIElementCreateSystemWide();
AXUIElementRef aMenu;
AXUIElementCopyElementAtPosition(_systemWideElement, aMenuPoint.x, aMenuPoint.y, &aMenu);
SafeCFRelease(_systemWideElement);
// Get menu items
CFTypeRef aMenuChildren;
AXUIElementCopyAttributeValue(aMenu, kAXVisibleChildrenAttribute, &aMenuChildren);
NSRunningApplication *app = [[NSRunningApplication runningApplicationsWithBundleIdentifier:[appDict objectForKey:#"BundleID"]] objectAtIndex:0];
for (NSInteger i = 0; i < CFArrayGetCount(aMenuChildren); i++) {
AXUIElementRef aMenuItem = [self copyAXUIElementFrom:aMenu role:kAXMenuItemRole atIndex:i];
CFTypeRef aTitle;
AXUIElementCopyAttributeValue(aMenuItem, kAXTitleAttribute, &aTitle);
// Supports chrome, safari, and finder
if ([(__bridge NSString *)aTitle isEqualToString:#"New Window"] || [(__bridge NSString *)aTitle isEqualToString:#"New Finder Window"]) {
AXUIElementPerformAction(aMenuItem, kAXPressAction);
NSInteger numberOfWindows = [self numberOfWindowsOpenFromApplicationWithPID:[app processIdentifier]];
// Wait until open
while ([self numberOfWindowsOpenFromApplicationWithPID:[app processIdentifier]] <= numberOfWindows) {
}
break;
}
}
SafeCFRelease(aMenu);
SafeCFRelease(aMenuChildren);
}
}
This is pretty complicated, but it works. I probably can't explain it, but I have stress tested this code and it works quite well.
Answer to the question "How to get an array of AXMenuItems from AXMenu?": aMenuChildren is the list of menu items. To be sure you could filter by role kAXMenuItemRole.
Answer to the question "Why does it not work?": The menu isn't a menu. When you inspect the menu in Axccessibility Inspector, it displays a warning "Parent does not report element as one of its children". The Dock app has one child, a list of dock items.

Reset nested uiscrollview zooms when scroll ends

Nested uiscrolls in a larger uiscroll need to, when zoomed, reset zoom level when they are off screen. I am trying to reset all of them when the scrolling ends but no luck. Any ideas?
myScrollview = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width,self.view.frame.size.height)];
myScrollview.pagingEnabled = YES;
myScrollview.scrollEnabled =YES;
myScrollview.clipsToBounds = NO;
myScrollview.indicatorStyle = UIScrollViewIndicatorStyleWhite;
myScrollview.showsHorizontalScrollIndicator = YES;
myScrollview.backgroundColor = [UIColor blackColor];
myScrollview.delegate = self;
NSInteger viewcount=4;
NSArray *images = [NSArray arrayWithObjects:[UIImage imageNamed:#"01.png"],[UIImage imageNamed:#"02.png"],[UIImage imageNamed:#"03.png"],[UIImage imageNamed:#"04.png"],nil];
for (int i = 0; i <viewcount; i++)
{
CGFloat x = i * self.view.frame.size.width;
subView = [[UIScrollView alloc]initWithFrame:CGRectMake(x, 0, self.view.frame.size.width, self.view.frame.size.height)];
[subView setBackgroundColor:[UIColor blackColor]];
[subView setCanCancelContentTouches:NO];
subView.clipsToBounds = NO; // default is NO, we want to restrict drawing within our scrollview
subView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
aImageView = [[UIImageView alloc ] initWithImage:[images objectAtIndex:i]];
[self.aImageView setTag:viewcount];
[subView addSubview:aImageView];
[subView setContentSize:CGSizeMake(aImageView.frame.size.width, subView.frame.size.height)];
subView.minimumZoomScale = 1;
subView.maximumZoomScale = 3;
subView.delegate = self;
[subView setScrollEnabled:YES];
subView.contentSize = aImageView.frame.size;
[myScrollview addSubview:subView];
}
myScrollview.contentSize = CGSizeMake(self.view.frame.size.width*viewcount,self.view.frame.size.height);
[self.view addSubview:myScrollview];
}
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
NSLog (#"test");
UIView * view = nil;
view = [subView viewWithTag:0];
//return view;
return [scrollView.subviews objectAtIndex:0];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"Did scroll");
[self resetImageZoom];
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
= NSLog(#"Did end decal");
[self resetImageZoom];
}
-(void)resetImageZoom {
NSLog(#"Resetting any image zoom");
for(UIView *view in [myScrollview subviews]) {
//if([view isKindOfClass:[UIScrollView class]]) {
//[(UIScrollView*)view setZoomScale:1.0 animated:NO];
//}
view.transform = CGAffineTransformIdentity;
}
}
That did it...
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSLog(#"Did end dece");
for (UIView *view in scrollView.subviews) {
if([view isKindOfClass:[UIScrollView class]]) {
[(UIScrollView*)view setZoomScale:1.0 animated:NO];
}
}
}

GL_STACK_OVERFLOW (0x503) error Cocos2d

So I have it set up so when the characters health is < 100 (for testing purposes) it stop the scene and goes to the game over scene.
if (playerDataManager.playerHealth < 100) {
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:3 scene: [GameLogic scene]]];
}
However when the players health drops below 100, it goes to the new scene, but the FPS drops dramatically from 60 to 5.
I get a list of OpenGL error 0x0503 in -[EAGLView swapBuffers] then it stays frozen like that for about 40 seconds, then the FPS unfreeze and goes back out to 60 and I get a list of 2012-07-13 10:37:50.234 Tilegame[93513:10a03] cocos2d: removeChildByTag: child not found!
Then I can continue with the app like normal, going back to the main menu, starting a new game, then recreate the error.
#import "HelloWorldLayer.h"
#import "Menu.h"
#import "SimpleAudioEngine.h"
#import "LogCabinMap.h"
#import "LogShedMap.h"
#import "SaveData.h"
#import "pauseM.h"
#import "Player.h"
#import "GameLogic.h"
CCSprite *player;
CGPoint newPos;
int joyDegrees;
// HelloWorldLayer implementation
#implementation HelloWorldLayer
#synthesize tileMap = _tileMap;
#synthesize background = _background;
#synthesize buildings = _buildings;
#synthesize meta = _meta;
#synthesize player = _player;
#synthesize foreground = _foreground;
#synthesize numCollected = _numCollected;
#synthesize hud = _hud;
-(void) animateEnemy:(CCSprite*)enemy {
//speed of the enemy
ccTime actualDuration = .2;
id actionMove;
int distanceFromPlayer = ccpDistance(player.position, enemy.position);
if (distanceFromPlayer < 200) { //Check whether enemy can "see" Ninja before moving towards him.
//rotate to face the player
CGPoint diff = ccpSub(player.position,enemy.position);
float angleRadians = atanf((float)diff.y / (float)diff.x);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float cocosAngle = -1 * angleDegrees;
if (diff.x < 0) {
cocosAngle += 180;
}
enemy.rotation = cocosAngle;
actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(player.position,enemy.position)),10)];
} else {
actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(player.position,enemy.position)),0)];
}
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(enemyMoveFinished:)];
[enemy runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
}
// callback. starts another iteration of enemy movement.
- (void) enemyMoveFinished:(id)sender {
CCSprite *enemy = (CCSprite *)sender;
[self animateEnemy: enemy];
}
-(void)addEnemyAtX:(int)x y:(int)y {
CCSprite *enemy = [CCSprite spriteWithFile:#"enemy1.png"];
enemy.position = ccp(x, y);
[self addChild:enemy];
[self animateEnemy:enemy];
}
-(void)setViewpointCenter:(CGPoint) position {
CGSize winSize = [[CCDirector sharedDirector] winSize];
int x = MAX(position.x, winSize.width / 2);
int y = MAX(position.y, winSize.height / 2);
x = MIN(x, (_tileMap.mapSize.width * _tileMap.tileSize.width)
- winSize.width / 2);
y = MIN(y, (_tileMap.mapSize.height * _tileMap.tileSize.height)
- winSize.height/2);
CGPoint actualPosition = ccp(x, y);
CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
CGPoint viewPoint = ccpSub(centerOfView, actualPosition);
self.position = viewPoint;
}
-(id) init
{
if( (self=[super init] )) {
[[SimpleAudioEngine sharedEngine] preloadEffect:#"pickup.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"hit.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"move.caf"];
//[[SimpleAudioEngine sharedEngine] playBackgroundMusic:#"TileMap.caf"];
self.isTouchEnabled = YES;
self.tileMap = [CCTMXTiledMap tiledMapWithTMXFile:#"TileMap.tmx"];
self.background = [_tileMap layerNamed:#"Background"];
self.foreground = [_tileMap layerNamed:#"Foreground"];
self.buildings = [_tileMap layerNamed:#"Buildings"];
self.meta = [_tileMap layerNamed:#"Meta"];
_meta.visible = NO;
CCTMXObjectGroup *objects = [_tileMap objectGroupNamed:#"Objects"];
NSAssert(objects != nil, #"'Objects' object group not found");
//NSMutableDictionary * padPoints = [objects objectNamed:#"pad"];
NSMutableDictionary * padPoints;
SaveData * SaveDataManager = [SaveData sharedSaveDataManager];
for (int i = 0; i < 20; i ++) {
for (padPoints in [objects objects]) {
if ([[SaveDataManager.padArray objectAtIndex: i] intValue] == 1 ){
if ([[padPoints valueForKey:#"pad"] intValue] == i){
int x = [[padPoints valueForKey:#"x"] intValue];
int y = [[padPoints valueForKey:#"y"] intValue];
self.player = [CCSprite spriteWithFile:#"man.png"];
_player.position = ccp(x+16,y+16);
//[self addChild:_player];
}
}
}
}
// iterate through objects, finding all enemy spawn points
// create an enemy for each one
NSMutableDictionary * spawnPoints;
for (spawnPoints in [objects objects]) {
if ([[spawnPoints valueForKey:#"Enemy"] intValue] == 2){
int x = [[spawnPoints valueForKey:#"x"] intValue];
int y = [[spawnPoints valueForKey:#"y"] intValue];
[self addEnemyAtX:x+=16 y:y+=64];
}
}
player = [CCSprite spriteWithFile:#"man.png"];
player.position= _player.position;
[_tileMap addChild:player z: 0];
// [self addChild:player z:10];
[self setViewpointCenter:player.position];
[self addChild:_tileMap z:-1];
[self scheduleUpdate];
}
return self;
}
- (CGPoint)tileCoordForPosition:(CGPoint)position {
int x = position.x / _tileMap.tileSize.width;
int y = ((_tileMap.mapSize.height * _tileMap.tileSize.height) - position.y) / _tileMap.tileSize.height;
return ccp(x, y);
}
-(void)setPlayerPosition:(CGPoint)position {
Player * playerDataManager = [Player playerSaveDataManager];
CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [_meta tileGIDAt:tileCoord];
int x = player.position.x;
int y = player.position.y;
if (tileGid) {
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collision = [properties valueForKey:#"Collidable"];
if (collision && [collision compare:#"True"] == NSOrderedSame) {
//[[SimpleAudioEngine sharedEngine] playEffect:#"hit.caf"];
if (joyDegrees > 45 && joyDegrees < 135){
player.position = ccp(x,y-1);
}
if (joyDegrees > 135 && joyDegrees < 225){
player.position = ccp(x+1,y);
}
if (joyDegrees > 225 && joyDegrees < 315){
player.position = ccp(x,y+1);
}
if ((joyDegrees > 315 && joyDegrees < 360) || (joyDegrees > -1 && joyDegrees < 45)){
player.position = ccp(x-1,y);
}
return;
}
NSString *collectable = [properties valueForKey:#"Collectable"];
if (collectable && [collectable compare:#"True"] == NSOrderedSame) {
[[SimpleAudioEngine sharedEngine] playEffect:#"pickup.caf"];
[_meta removeTileAt:tileCoord];
[_foreground removeTileAt:tileCoord];
self.numCollected += 1;
[_hud numCollectedChanged:_numCollected];
playerDataManager.playerHealth -= 10;
}
NSString *Gate = [properties valueForKey:#"Gate"];
if (Gate && [Gate compare:#"1"] == NSOrderedSame) {
SaveData * SaveDataManager = [SaveData sharedSaveDataManager];
//[SaveDataManager.padArray removeAllObjects];
for (int i = 0; i < 20; i ++) {
[SaveDataManager.padArray replaceObjectAtIndex:i withObject:[NSNumber numberWithInt:0]];
}
[SaveDataManager.padArray replaceObjectAtIndex:1 withObject:[NSNumber numberWithInt:1]];
//[[CCDirector sharedDirector] replaceScene:[LogCabinMap scene]];
[[CCDirector sharedDirector] pushScene:[LogCabinMap scene]];
}
if (Gate && [Gate compare:#"2"] == NSOrderedSame) {
SaveData * SaveDataManager = [SaveData sharedSaveDataManager];
//[SaveDataManager.padArray removeAllObjects];
for (int i = 0; i < 20; i ++) {
[SaveDataManager.padArray replaceObjectAtIndex:i withObject:[NSNumber numberWithInt:0]];
}
[SaveDataManager.padArray replaceObjectAtIndex:1 withObject:[NSNumber numberWithInt:1]];
//[[CCDirector sharedDirector] replaceScene:[LogShedMap scene]];
[[CCDirector sharedDirector] pushScene:[LogShedMap scene]];
}
}
}
//[[SimpleAudioEngine sharedEngine] playEffect:#"move.caf"];
player.position = position;
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
}
-(void) update:(ccTime)deltaTime {
[self setPlayerPosition:newPos];
[self setViewpointCenter:player.position];
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
self.tileMap = nil;
self.background = nil;
self.foreground = nil;
self.buildings = nil;
self.meta =nil;
self.player = nil;
player = nil;
self.hud = nil;
// don't forget to call "super dealloc"
[super dealloc];
}
#end
#implementation HelloWorldHud
#synthesize background;
#synthesize background2;
#synthesize health;
#synthesize baseScaleFactor;
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldHud *layer = [HelloWorldHud node];
// add layer as a child to scene
[scene addChild: layer z:2];
HelloWorldLayer *hud = [HelloWorldLayer node];
[scene addChild: hud z:1];
//layer.hud = hud;
// return the scene
return scene;
}
-(void)initJoystick {
SneakyJoystickSkinnedBase *joystickBase = [[[SneakyJoystickSkinnedBase alloc] init] autorelease];
joystickBase.backgroundSprite = [CCSprite spriteWithFile:#"JoyB.png"];
joystickBase.thumbSprite = [CCSprite spriteWithFile:#"JoyS.png"];
joystickBase.joystick = [[SneakyJoystick alloc] initWithRect: CGRectMake(0, 0, 128, 128)];
joystickBase.position = ccp(55, 55);
[self addChild:joystickBase];
leftJoystick = [[joystickBase.joystick retain] autorelease];
}
-(void) update:(ccTime)deltaTime {
Player *playerDataManager = [Player playerSaveDataManager];
CGPoint scaledVelocity = ccpMult(leftJoystick.velocity, 100);
CGPoint newPosition = ccp(player.position.x + scaledVelocity.x * deltaTime, player.position.y + scaledVelocity.y * deltaTime);
if (leftJoystick.velocity.x == 0 && leftJoystick.velocity.y == 0 ){
if (!playerDataManager.standStill) {
[player stopAllActions];
playerDataManager.walkUp = FALSE;
playerDataManager.walkDown = FALSE;
playerDataManager.walkRight = FALSE;
playerDataManager.walkLeft = FALSE;
playerDataManager.standStill = TRUE;
[player runAction:playerDataManager.standAction];
}
}
if (leftJoystick.degrees > 45 && leftJoystick.degrees < 135){
if (!playerDataManager.walkUp) {
[player stopAllActions];
playerDataManager.walkUp = TRUE;
playerDataManager.walkDown = FALSE;
playerDataManager.walkRight = FALSE;
playerDataManager.walkLeft = FALSE;
playerDataManager.standStill = FALSE;
[player runAction:playerDataManager.walkUpAction];
}
}
if (leftJoystick.degrees > 135 && leftJoystick.degrees < 225){
if (!playerDataManager.walkLeft) {
[player stopAllActions];
playerDataManager.walkUp = FALSE;
playerDataManager.walkDown = FALSE;
playerDataManager.walkRight = FALSE;
playerDataManager.walkLeft = TRUE;
playerDataManager.standStill = FALSE;
[player runAction:playerDataManager.walkLeftAction];
}
}
if (leftJoystick.degrees > 225 && leftJoystick.degrees < 315){
if (!playerDataManager.walkDown) {
[player stopAllActions];
playerDataManager.walkUp = FALSE;
playerDataManager.walkDown = TRUE;
playerDataManager.walkRight = FALSE;
playerDataManager.walkLeft = FALSE;
playerDataManager.standStill = FALSE;
[player runAction:playerDataManager.walkDownAction];
}
}
if (((leftJoystick.degrees > 315 && leftJoystick.degrees < 360) && (leftJoystick.velocity.x != 0 && leftJoystick.velocity.y != 0 )) || ((leftJoystick.degrees > -1 && leftJoystick.degrees < 45) && (leftJoystick.velocity.x != 0 && leftJoystick.velocity.y != 0 ))){
if (!playerDataManager.walkRight) {
[player stopAllActions];
playerDataManager.walkUp = FALSE;
playerDataManager.walkDown = FALSE;
playerDataManager.walkRight = TRUE;
playerDataManager.walkLeft = FALSE;
playerDataManager.standStill = FALSE;
[player runAction:playerDataManager.walkRightAction];
}
}
float scaleFactor = playerDataManager.playerHealth/baseScaleFactor;
health.scaleX = playerDataManager.playerHealth/1;
newPos = newPosition;
joyDegrees = leftJoystick.degrees;
if (playerDataManager.playerHealth < 100) {
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:3 scene: [GameLogic scene]]];
[[CCDirector sharedDirector] replaceScene: [GameLogic scene]];
}
}
-(void) scroll:(ccTime)dt
{
//move 30*dt px vertically
if (background.position.y<background2.position.y){
background.position = ccp(background.contentSize.width/2, background.position.y - 225*dt);
background2.position = ccp(background2.contentSize.width/2, background.position.y+background.contentSize.height);
}else{
background2.position = ccp(background2.contentSize.width/2, background2.position.y- 225*dt);
background.position = ccp(background.contentSize.width/2, background2.position.y+background2.contentSize.height);
}
//reset offscreen position
if (background.position.y <-background.contentSize.height/2)
{
background.position = ccp(background.contentSize.height/2,background2.position.y+background2.contentSize.height);
}else if (background2.position.y < -background2.contentSize.height/2)
{
background2.position = ccp(background2.contentSize.height/2, background.position.y+background.contentSize.height);
}
}
-(id) init
{
if ((self = [super init])) {
CGSize size = [CCDirector sharedDirector].winSize;
background = [CCSprite spriteWithFile:#"rain.png"];
background2 = [CCSprite spriteWithFile:#"rain.png"];
[background.texture setAliasTexParameters];
[background2.texture setAliasTexParameters];
//position background sprites
background.position = ccp(background.contentSize.height/2,background.contentSize.width/2);
background2.position = ccp(size.width,0);
//schedule to move background sprites
//[self schedule:#selector(scroll:)];
//adding them to the main layer
//[self addChild:background z:-1];
//[self addChild:background2 z:-1];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"GUI.plist"];
CCSpriteBatchNode *GUISpriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"GUI.png"];
[self addChild:GUISpriteSheet];
CCSprite * healthBar = [CCSprite spriteWithSpriteFrameName:#"bar.png"];
CCSprite * cap = [CCSprite spriteWithSpriteFrameName:#"cap.png"];
CCSprite * endCap = [CCSprite spriteWithSpriteFrameName:#"endcap.png"];
health = [CCSprite spriteWithSpriteFrameName:#"red.png"];
healthBar.anchorPoint = ccp(0,1);
health.anchorPoint = ccp(0,1);
cap.anchorPoint = ccp(0,1);
endCap.anchorPoint = ccp(0,1);
healthBar.position = ccp(1 , size.height);
healthBar.scaleX = 25;
cap.position = ccp(0 , size.height);
int width = [healthBar boundingBox].size.width;
endCap.position = ccp(width , size.height);
health.position = ccp(1, size.height-2);
baseScaleFactor = width;
health.scaleX = baseScaleFactor;
[self addChild:healthBar];
[self addChild:cap];
[self addChild:endCap];
[self addChild:health];
CGSize winSize = [[CCDirector sharedDirector] winSize];
label = [CCLabelTTF labelWithString:#"0" dimensions:CGSizeMake(50, 20)
alignment:UITextAlignmentRight fontName:#"Verdana-Bold"
fontSize:18.0];
label.color = ccc3(0,0,0);
int margin = 10;
label.position = ccp(winSize.width - (label.contentSize.width/2)
- margin, label.contentSize.height/2 + margin);
[self addChild:label];
CCMenuItemFont * pause = [CCMenuItemFont itemFromString:#"Pause" target:self selector:#selector(pause:)];
CCMenu *menu = [CCMenu menuWithItems: pause, nil];
pause.position = ccp(206,142);
[self addChild:menu];
CCSprite *pix = [CCSprite spriteWithFile:#"pix.png"];
pix.position = ccp(size.width/2, size.height/2);
//pix.scale = 50000;
[self addChild:pix z:1];
//[pix runAction:[CCTintTo actionWithDuration:10 red:255 green:0 blue:0]];
//[pix runAction:[CCScaleTo actionWithDuration:15 scale:500]];
pix.color = ccc3(255, 255, 255);
[self scheduleUpdate];
[self initJoystick];
}
return self;
}
- (void)numCollectedChanged:(int)numCollected {
[label setString:[NSString stringWithFormat:#"%d", numCollected]];
// [label setString:[NSString stringWithFormat:#"%d", score]];
}
- (void) pause: (id) sender
{
[[CCDirector sharedDirector] pushScene:[pauseM scene]];
}
- (void) dealloc
{
self.background = nil;
self.background2 = nil;
self.health = nil;
self.meta =nil;
player = nil;
// don't forget to call "super dealloc"
[super dealloc];
}
#end
Above is the .m scene that is running when the problem occurs and below is the GameLogic.m
#import "GameLogic.h"
#import "Player.h"
#import "Menu.h"
#implementation GameLogic
+(id) scene
{
CCScene *scene = [CCScene node];
GameLogic *layer = [GameLogic node];
[scene addChild: layer];
return scene;
}
+(id) gameLogicSaveDataManager {
static id gameLogicSaveDataManager = nil;
if (gameLogicSaveDataManager == nil) {
gameLogicSaveDataManager = [[self alloc] init];
}
return gameLogicSaveDataManager;
}
-(id) init
{
if( (self=[super init] )) {
CCSprite *bg = [CCSprite spriteWithFile:#"bg.jpg"];
bg.anchorPoint = ccp(0,0);
id fadeIn = [CCFadeIn actionWithDuration:3];
[self addChild:bg];
[bg runAction: fadeIn];
CCLayer *menuLayer = [[[CCLayer alloc] init] autorelease];
[self addChild:menuLayer];
CCMenuItemImage *home = [CCMenuItemImage
itemFromNormalImage:#"bg.jpg"
selectedImage:#"bg.jpg"
target:self
selector:#selector(home:)];
CCMenu *menu = [CCMenu menuWithItems: home, nil];
home.position = ccp(0,0);
[menuLayer addChild: menu];
}
return self;
}
- (void) home: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Menu scene]];
}
- (void) dealloc
{
[super dealloc];
}
#end
OpenGL error 0x503 means GL_STACK_OVERFLOW, it means you are pushing too many things onto the opengl stack.
I see several instances of pushScene in your code, but no instances of pop anything anywhere. You cannot just keep pushing things indefinitely without popping them, or you will get this error.
We need to what is currently running in your scene, how it has been allocated and how it is released. We also need to know the same information for the new loaded scene GameLogic. Edit your question and add these data.

How can i convert NSBezierPath to CGPath

How can i convert between NSBezierPath to CGPath.
Thanks.
Right from Apple documentation: Creating a CGPathRef From an NSBezierPath Object
Here is the relevant code.
#implementation NSBezierPath (BezierPathQuartzUtilities)
// This method works only in OS X v10.2 and later.
- (CGPathRef)quartzPath
{
int i, numElements;
// Need to begin a path here.
CGPathRef immutablePath = NULL;
// Then draw the path elements.
numElements = [self elementCount];
if (numElements > 0)
{
CGMutablePathRef path = CGPathCreateMutable();
NSPoint points[3];
BOOL didClosePath = YES;
for (i = 0; i < numElements; i++)
{
switch ([self elementAtIndex:i associatedPoints:points])
{
case NSMoveToBezierPathElement:
CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
break;
case NSLineToBezierPathElement:
CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
didClosePath = NO;
break;
case NSCurveToBezierPathElement:
CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
didClosePath = NO;
break;
case NSClosePathBezierPathElement:
CGPathCloseSubpath(path);
didClosePath = YES;
break;
}
}
// Be sure the path is closed or Quartz may not do valid hit detection.
if (!didClosePath)
CGPathCloseSubpath(path);
immutablePath = CGPathCreateCopy(path);
CGPathRelease(path);
}
return immutablePath;
}
#end
Bug Reporter
rdar://15758302: NSBezierPath to CGPath.
The syntax in Xcode 8 GM has been further simplified, code modified from rob-mayoff's answer above. Using this and a helper for addLine(to point: CGPoint) I am sharing drawing code cross platform.
extension NSBezierPath {
public var cgPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< elementCount {
let type = element(at: i, associatedPoints: &points)
switch type {
case .moveTo:
path.move(to: points[0])
case .lineTo:
path.addLine(to: points[0])
case .curveTo:
path.addCurve(to: points[2], control1: points[0], control2: points[1])
case .closePath:
path.closeSubpath()
#unknown default:
continue
}
}
return path
}
}
This works in Swift 3.1 and later:
import AppKit
public extension NSBezierPath {
public var cgPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount {
let type = self.element(at: i, associatedPoints: &points)
switch type {
case .moveToBezierPathElement: path.move(to: points[0])
case .lineToBezierPathElement: path.addLine(to: points[0])
case .curveToBezierPathElement: path.addCurve(to: points[2], control1: points[0], control2: points[1])
case .closePathBezierPathElement: path.closeSubpath()
}
}
return path
}
}
For macOS better use - CGMutablePath
But, if you want cgPath for NSBezierPath:
Swift 5.0
extension NSBezierPath {
var cgPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount {
let type = self.element(at: i, associatedPoints: &points)
switch type {
case .moveTo:
path.move(to: points[0])
case .lineTo:
path.addLine(to: points[0])
case .curveTo:
path.addCurve(to: points[2], control1: points[0], control2: points[1])
case .closePath:
path.closeSubpath()
#unknown default:
break
}
}
return path
}
}
Here is a Swift version if anyone else finds a need for it:
extension IXBezierPath {
// Adapted from : https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Paths/Paths.html#//apple_ref/doc/uid/TP40003290-CH206-SW2
// See also: http://www.dreamincode.net/forums/topic/370959-nsbezierpath-to-cgpathref-in-swift/
func CGPath(forceClose forceClose:Bool) -> CGPathRef? {
var cgPath:CGPathRef? = nil
let numElements = self.elementCount
if numElements > 0 {
let newPath = CGPathCreateMutable()
let points = NSPointArray.alloc(3)
var bDidClosePath:Bool = true
for i in 0 ..< numElements {
switch elementAtIndex(i, associatedPoints:points) {
case NSBezierPathElement.MoveToBezierPathElement:
CGPathMoveToPoint(newPath, nil, points[0].x, points[0].y )
case NSBezierPathElement.LineToBezierPathElement:
CGPathAddLineToPoint(newPath, nil, points[0].x, points[0].y )
bDidClosePath = false
case NSBezierPathElement.CurveToBezierPathElement:
CGPathAddCurveToPoint(newPath, nil, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y )
bDidClosePath = false
case NSBezierPathElement.ClosePathBezierPathElement:
CGPathCloseSubpath(newPath)
bDidClosePath = true
}
if forceClose && !bDidClosePath {
CGPathCloseSubpath(newPath)
}
}
cgPath = CGPathCreateCopy(newPath)
}
return cgPath
}
I can't figure out why the accepted answer adds some sophisticated close-path logic (maybe it is needed under some circumstances), but for those who just need a pristine conversion of the path, here is cleaned version of that code, implemented as a regular method:
- (CGMutablePathRef)CGPathFromPath:(NSBezierPath *)path
{
CGMutablePathRef cgPath = CGPathCreateMutable();
NSInteger n = [path elementCount];
for (NSInteger i = 0; i < n; i++) {
NSPoint ps[3];
switch ([path elementAtIndex:i associatedPoints:ps]) {
case NSMoveToBezierPathElement: {
CGPathMoveToPoint(cgPath, NULL, ps[0].x, ps[0].y);
break;
}
case NSLineToBezierPathElement: {
CGPathAddLineToPoint(cgPath, NULL, ps[0].x, ps[0].y);
break;
}
case NSCurveToBezierPathElement: {
CGPathAddCurveToPoint(cgPath, NULL, ps[0].x, ps[0].y, ps[1].x, ps[1].y, ps[2].x, ps[2].y);
break;
}
case NSClosePathBezierPathElement: {
CGPathCloseSubpath(cgPath);
break;
}
default: NSAssert(0, #"Invalid NSBezierPathElement");
}
}
return cgPath;
}
Btw, I needed this to implement "NSBezierPath stroke contains point" method.
I've looked for this conversion to call CGPathCreateCopyByStrokingPath(), that converts NSBezierPath stroke outline to regular path, so you can test hits on strokes too, and here is the solution:
// stroke (0,0) to (10,0) width 5 --> rect (0, -2.5) (10 x 5)
NSBezierPath *path = [[NSBezierPath alloc] init];
[path moveToPoint:NSMakePoint(0.0, 0.0)];
[path lineToPoint:NSMakePoint(10.0, 0.0)];
[path setLineWidth:5.0];
CGMutablePathRef cgPath = [self CGPathFromPath:path];
CGPathRef strokePath = CGPathCreateCopyByStrokingPath(cgPath, NULL, [path lineWidth], [path lineCapStyle],
[path lineJoinStyle], [path miterLimit]);
CGPathRelease(cgPath);
NSLog(#"%#", NSStringFromRect(NSRectFromCGRect(CGPathGetBoundingBox(strokePath))));
// {{0, -2.5}, {10, 5}}
CGPoint point = CGPointMake(1.0, 1.0);
BOOL hit = CGPathContainsPoint(strokePath, NULL, point, (bool)[path windingRule]);
NSLog(#"%#: %#", NSStringFromPoint(NSPointFromCGPoint(point)), (hit ? #"yes" : #"no"));
// {1, 1}: yes
CGPathRelease(strokePath);
This is similar to QPainterPathStroker from Qt, but for NSBezierPath.
c# Xamarin
private CGPath convertNSBezierPathToCGPath(NSBezierPath sourcePath)
{
CGPath destinationPath = new CGPath();
int i, numElements;
// Then draw the path elements.
numElements = (int)Convert.ToInt64(sourcePath.ElementCount);
if (numElements > 0)
{
CGPath path = new CGPath();
CGPoint[] points;
bool didClosePath = true;
for (i = 0; i < numElements; i++)
{
switch (sourcePath.ElementAt(i, out points))
{
case NSBezierPathElement.MoveTo:
path.MoveToPoint(points[0]);
break;
case NSBezierPathElement.LineTo:
path.MoveToPoint(points[0]);
didClosePath = false;
break;
case NSBezierPathElement.CurveTo:
path.AddCurveToPoint(cp1: points[0], cp2: points[1], points[2]);
didClosePath = false;
break;
case NSBezierPathElement.ClosePath:
path.CloseSubpath();
didClosePath = true;
break;
}
}
if (!didClosePath)
path.CloseSubpath();
destinationPath = new CGPath(path);
}
return destinationPath;
}

Resources