crash on CPTScatterPlotDelegate in CorePlot - cocoa

I am trying to overlay multiple graphs in one plot and I am getting crashes (EXC_BAD_ACCESS) on the CPTScatterPlotDelegate below in class CPTScatterPlot.m
-(void)renderAsVectorInContext:(nonnull CGContextRef)context {
...
// Draw line
if ( theLineStyle ) {
CGPathRef dataLinePath = [self newDataLinePathForViewPoints:viewPoints indexRange:viewIndexRange baselineYValue:CPTNAN];
// Give the delegate a chance to prepare for the drawing.
id<CPTScatterPlotDelegate> theDelegate = self.delegate;
....
}
...
}
The Same in CPTLegendDelegate in class CPTPlot.m
-(void)drawSwatchForLegend:(nonnull CPTLegend *)legend atIndex:(NSUInteger)idx inRect:(CGRect)rect inContext:(nonnull CGContextRef)context
{
id<CPTLegendDelegate> theDelegate = (id<CPTLegendDelegate>)self.delegate;
...
}
I am using CorePlot 2.1, and I have modified the renderInGraphHostingView in file SimpleScatterPlot.m in examples/CorePlotGallery as follows:
-(void)renderInGraphHostingView:(nonnull CPTGraphHostingView *)hostingView withTheme:(nullable CPTTheme *)theme animated:(BOOL)animated {
...
static CPTGraph *graph = nil;
if( initialize ) {
graph = [[CPTXYGraph alloc] initWithFrame:bounds];
...
}
[self addGraph:graph toHostingView:hostingView];
theme = [CPTTheme themeNamed:kCPTDarkGradientTheme];
...
}
So every time I want to draw a new line of data, I use the same graph.
The problem is random and sometimes program crashes when I draw the second line, sometimes in the third, but it always works fine for the first graph. It also depends on the compilation.
Any ideas?
Thanks in Advance

The delegate property holds a weak reference to the delegate object. Make sure the delegate isn't being deallocated between graph updates.

Related

Receive notification for power cord on/off in OSX Cocoa for the laptop

ALL,
Does OSX sends any notification when the power cord is plugged/unplugged in the Mac laptop? Or plugging/unplugging the laptop from the docking station?
It does send something on shutdown/reboot/wake-up/etc, but I didn't find anything for that specific event.
Am I missing something? Or this is not available?
TIA!
There are no high-level notifications, but the IOPowerSources framework can post a message to your run loop whenever the power state changes.
My project has multiple PowerCondition objects that monitor various aspects of the current power (A/C vs. Battery, how much battery is left, and so on).
Here's a fragment of the PowerCondition class that shows how to observe power source change events via the run loop and turn that into a notification that multiple objects can observe (this is fairly old Obj-C):
#define kPowerConditionChangedNotification #"PowerConditionChanged"
static NSUInteger PowerChangeListenerCount = 0;
static CFRunLoopSourceRef PowerChangeSource = NULL;
static void PowerChangeCallback(void *context)
{
// Called by CFRunLoopSourceRef when something changes
// Post a notification so that all PowerCondition observers can reevaluate
[[NSNotificationCenter defaultCenter] postNotificationName:kPowerConditionChangedNotification object:nil];
}
#interface PowerCondition
{
BOOL listening;
//...
}
- (void)powerStateDidChangeNotification:(NSNotification*)notification;
#end
// ...
#implementation PowerCondition
- (void)startMonitoringCondition
{
if (!listening)
{
// Observe the power condition change notification
DebugLogger(#"condition '%#'",[self description]);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(powerStateDidChangeNotification:)
name:kPowerConditionChangedNotification
object:nil];
if (PowerChangeListenerCount++==0)
{
// This is the first observer: create and install the run loop source that will fire the notification
BetaAssert(PowerChangeSource==NULL,#"zero PowerChangeListenerCount, but PowerChangeSource!=NULL");
PowerChangeSource = IOPSNotificationCreateRunLoopSource(PowerChangeCallback,NULL);
CFRunLoopAddSource([[NSRunLoop mainRunLoop] getCFRunLoop],PowerChangeSource,kCFRunLoopCommonModes);
DebugLoggerMessage(#"installed PowerChangeSource(PowerChangeCallback) in run loop");
}
listening = YES;
previousTestState = -1; // neither YES nor NO
}
}
- (void)stopMonitoringCondition
{
if (listening)
{
// Stop observing power condition change notifications
PowerLogger(#"condition '%#'",[self description]);
[[NSNotificationCenter defaultCenter] removeObserver:self];
BetaAssert(PowerChangeListenerCount!=0,#"PowerChangeListenerCount==0");
if (--PowerChangeListenerCount==0)
{
// This was the last power change observer: remove the run loop source the fires the notifications
BetaAssertNotNil(PowerChangeSource);
CFRunLoopRemoveSource([[NSRunLoop mainRunLoop] getCFRunLoop],PowerChangeSource,kCFRunLoopCommonModes);
CFRelease(PowerChangeSource);
PowerChangeSource = NULL;
DebugLoggerMessage(#"removed PowerChangeSource(PowerChangeCallback) from run loop");
}
listening = NO;
}
}
- (void)powerStateDidChangeNotification:(NSNotification*)notification
{
// Evaluate power state here
BOOL battery = NO; // assume unlimited/external power
double capacityRemaining = 1.0; // assume 100%
// First, determine if the system's active power source is AC or battery
CFTypeRef powerBlob = IOPSCopyPowerSourcesInfo();
CFArrayRef powerSourcesRef = IOPSCopyPowerSourcesList(powerBlob);
CFIndex count = CFArrayGetCount(powerSourcesRef);
if (count!=0)
{
// There's precious little explination what the meaning of the different power sources
// are or why there would be more than one. As far as I can tell, everything can be
// determined by obtaining the first (and probably only) power source description.
NSDictionary* powerInfo = (__bridge id)IOPSGetPowerSourceDescription(powerBlob,CFArrayGetValueAtIndex(powerSourcesRef,0));
if (![powerInfo[#kIOPSPowerSourceStateKey] isEqualToString:#kIOPSACPowerValue])
{
// Power source is not AC (must be battery or UPS)
battery = YES;
// Calculate the remaining capacity, as a fraction
NSNumber* capacityValue = powerInfo[#kIOPSCurrentCapacityKey];
NSNumber* maxValue = powerInfo[#kIOPSMaxCapacityKey];
if (capacityValue!=nil && maxValue!=nil)
capacityRemaining = [capacityValue doubleValue]/[maxValue doubleValue];
DebugLogger(#"power source is '%#', battery=%d, capacity=%#, max=%#, remaining=%f%%",
powerInfo[#kIOPSPowerSourceStateKey],
(int)battery,
capacityValue,maxValue,capacityRemaining*100);
}
}
// ...
}
Note that the callback is installed on the main run loop, so the notification will always be posted on the main thread.

Show current frame rate of a MTKView

I know that in SceneKit, you can enable a banner on the side of the SKView to look at real time frame rates and other useful debugging information. But what about MTKView? I don't seem to find such a property to enable, or how I can query the current frame rate. (Because I am rendering something that have a frame rate of 0.5fps or so)
I don't think there is simple flag for you. Because you control the complete rendering pipeline when creating a command buffer, Metal can't know where to inject a rendering pass with some custom text.
You could inject your own rendering pass (based on a flag like var showDebugInformation = true) in your pipeline, but that sounds like a bit of work.
I would probably monitor frame times manually in the draw method and update a label every draw. A rough outline could look like this:
var previousFrameAtTime: Date
let lastFrameTime = CurrentValueSubject<TimeInterval, Never>(.infinity)
func draw(in view: MTKView) {
lastFrameTime.send(Date().timeIntervalSince(previousFrameAtTime))
previousFrameAtTime = Date()
// ...
}
Then you can observe this value in your view, something like this:
import Combine
class MyViewController: UIViewController {
let label = UILabel()
var cancellables: [AnyCancellable] = []
func subscribeToFrameTime() {
renderer.lastFrameTime
.sink { label.text = "\($0 * 1000) ms." }
.store(in: &cancellables)
}
}

Making an NSTableView behave like a WPF StackPanel

I'm trying to implement a vertical StackPanel equivalent in my MonoMac project and am having trouble figuring it out. I am not using interface builder but creating the controls programmatically (this is a restriction). I tried using a CollectionView but all items in that control are sizing to the same height.
Looking around the internet, it seems NSTableView is the way to go, but I'm not sure how to do it, especially with C# while using a MonoMac target. CollectionView was somewhat more straightforward with the CollectionViewItem.ItemPrototype allowing me to create the views I want to render. But with an NSTableView, it seems like I can only specify a data source that returns the NSObjects I want to display. How do I grab this data and then bind them to the view I want to stick in there?
I would prefer C# code but I'm at a stage where I'll accept any and all help!
I was finally able to get it working. Here is some code for anyone who wants to try it out. Basically, we need to write NSTableViewDelegates for the required functions. This implementation also doesn't cache the controls or anything. The Cocoa API documentation mentioned using an identifier to reuse the control, or something, but the identifier field is get-only in MonoMac.
I also ended up implementing my NSTableViewDelegate functions in my data-source itself which I am sure is not kosher at all, but I'm not sure what the best practice is.
Here's the data source class:
class MyTableViewDataSource : NSTableViewDataSource
{
private NSObject[] _data;
// I'm coming from an NSCollectionView, so my data is already in this format
public MyTableViewDataSource(NSObject[] data)
{
_data = data;
}
public override int GetRowCount(NSTableView tableView)
{
return _data.Length;
}
#region NSTableViewDelegate Methods
public NSView GetViewForItem(NSTableView tableView, NSTableColumn tableColumn, int row)
{
// MyViewClass extends NSView
MyViewClass result = tableView.MakeView("MyView", this) as MyViewClass;
if (result == null)
{
result = new MyViewClass(_data[row]);
result.Frame = new RectangleF(0, 0, tableView.Frame.Width, 100); // height doesn't matter since that is managed by GetRowHeight
result.NeedsDisplay = true;
// result.Identifier = "MyView"; // this line doesn't work because Identifier only has a getter
}
return result;
}
public float GetRowHeight(NSTableView tableView, int row)
{
float height = FigureOutHeightFromData(_data[row]); // run whatever algorithm you need to get the row's height
return height;
}
#endregion
}
And here's the snippet that programmatically creates the table:
var tableView = new NSTableView();
var dataSource = new MyTableViewDataSource();
tableView.DataSource = dataSource;
tableView.HeaderView = null; // get rid of header row
tableView.GetViewForItem = dataSource.GetViewForItem;
tableView.GetRowHeight = dataSource.GetRowHeight;
AddSubView(tableView);
So, it is not a perfect StackPanel because one needs to manually calculate row heights, but it's better than nothing.

use CGPoint as an array

I want to use an function to pass a lot of points to another function, but the Xcode has errors on the line with: CGContextAddLines......
the add points is filled with information like:
CGPoint addPoints[] = {
CGPointMake(10,10),
CGPointMake(10,10),
}
the use the
-(void) constructPoints:(CGContextRef) context withPoints:(CGPoint) addPoints {
// do some context set attributes, color
// and
CGContextAddLines(context, addPoints, sizeof(addPoints)/sizeof(addPoints[0]));
// and draw-it
}
Try it like this:
-(void) constructPoints:(CGContextRef) context withPoints:(CGPoint[]) addPoints numPoints:(int) size {
// do some context set attributes, color
// and
CGContextAddLines(context, addPoints, size);
// and draw-it
}
Then on your call:
[self constructPoints:yourContext withPoints:addPoints numPoints:sizeof(addPoints)/sizeof(addPoints[0])];

How to hide a divider of nssplitview?

Now I want to hide or show with my condition a divider when my app run. used this delegate method:
- (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex
{
if (A)
return YES;
else
return NO;
}
but it didn't work , why? How to used this method? Thank you very much!
Further to #carmin’s note, above, overriding the NSSplitView dividerThickness property is the only thing that worked for me (specifically, returning NSRectZero from the splitView:effectiveRect:forDrawnRect:ofDividerAtIndex: NSSplitView delegate method — as detailed here – didn’t work and resulted in floating dividers disjointed from the views themselves).
Here’s the code in Swift:
override var dividerThickness:CGFloat
{
get { return 0.0 }
}
The split view sends that message to its delegate, to ask the delegate whether it should hide that divider. So, be the delegate, and answer the split view's question.
Be sure to check out the documentation. It's possible that that message won't accomplish what you want it to. The documentation lists everything you can do by responding to that message.
You can overload NSSplitView-dividerThickness and return 0 to hide all of the dividers. You can overload NSSplitView-drawDividerInRect: to have individual control over the dividers (choosing to allow super to draw the divider or not). These choices work even when the subviews are visible.
Here's how to do it in Obj-C that doesn't involve subclassing. Make sure that you've got the SplitView delegate in IB connected.
Then in your delegate class:
-(NSRect)splitView:(NSSplitView *)splitView effectiveRect:(NSRect)proposedEffectiveRect forDrawnRect:(NSRect)drawnRect ofDividerAtIndex:(NSInteger)dividerIndex
{
if ( [_splitView subviews][1].isHidden ==YES || [[_splitView subviews][1] frame].size.height < 50) //closed or almost closed
{
return NSZeroRect;
}
return proposedEffectiveRect;
}
- (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex
{
if ( [_splitView subviews][1].isHidden ==YES || [[_splitView subviews][1] frame].size.height < 50)
{
return YES;
}
return NO;
}
This will hide the divider when the split view is closed, but show it when it is open.
If you don't want them to be able to drag it even when its open, just cut out all the code in the first method and return only NSZeroRect. Do the same in the second method and only return YES.
For the sake of posterity, working with Swift you can call the delegate function splitView(_:effectiveRect:forDrawnRect:ofDividerAtIndex:) and just have it return an empty NSRect
override func splitView(_ splitView: NSSplitView, effectiveRect proposedEffectiveRect: NSRect, forDrawnRect drawnRect: NSRect, ofDividerAt dividerIndex: Int) -> NSRect {
if dividerIndex == 1 {
return NSRect()
}
return super.splitView(splitView, effectiveRect: proposedEffectiveRect, forDrawnRect: drawnRect, ofDividerAt: dividerIndex)
}
Use this class in Custom class of NSSplitView:
class customSplitView: NSSplitView {
override var dividerThickness: CGFloat {
return 0
}
}
For me it worked!

Resources