Thank you Eric, that seems to do the trick! For posterity, here is the simplest way I could figure to set the whole thing up in Swift (I'm not too familiar with the differences between CGFloat/Decimal/NSNumber, so if anyone has a better suggestion I'll welcome it. I dealt all in Decimal to avoid a bunch of conversions. Also, I'm sure there is a better way to do the Notification observer)
First, in the top level class used to create the CPTGraph, I added the following variables to keep track of the plot area size (initialized to -1.0 so I can avoid running the resize callback on initialization):
var plotAreaSize: (Decimal, Decimal) = (-1.0, -1.0)
var newPlotAreaSize: (Decimal, Decimal) {
if let pa = graph.plotAreaFrame.plotArea {
return (pa.widthDecimal, pa.heightDecimal) } else { return plotAreaSize }
}
Then I add a function to actually perform the resizing:
private func enforceEqualAxes(xScale: Decimal, yScale: Decimal) {
let plotSpace = graph.defaultPlotSpace as! CPTXYPlotSpace
let maxX = plotSpace.xRange.lengthDecimal*xScale
let maxY = plotSpace.yRange.lengthDecimal*yScale
let xLoc = plotSpace.xRange.locationDecimal
let yLoc = plotSpace.yRange.locationDecimal
if xScale == 1.0 && yScale == 1.0 { // on initialization, scale to fit the whole plot let maxSize = [maxX, maxY].max()!
plotSpace.xRange = CPTPlotRange(locationDecimal: xLoc, lengthDecimal: maxSize)
plotSpace.yRange = CPTPlotRange(locationDecimal: yLoc, lengthDecimal: maxSize)
}
if xScale != 1.0 {
plotSpace.xRange = CPTPlotRange(locationDecimal: xLoc, lengthDecimal: maxX)
}
if yScale != 1.0 {
plotSpace.yRange = CPTPlotRange(locationDecimal: yLoc, lengthDecimal: maxY)
}
}
Add a callback function to run the resizing when the plot area changes:
@objc func didResizePlotArea(notification: NSNotification) {
guard let plotArea = notification.object as? CPTPlotArea else { return }
if plotAreaSize.0 > 0 && plotAreaSize.1 > 0 { // Only run after initialization
enforceEqualAxes(xScale: newPlotAreaSize.0/plotAreaSize.0, yScale: newPlotAreaSize.1/plotAreaSize.1)
}
plotAreaSize = newPlotAreaSize
}
Finally, at the initialization of the graph, run the resizer the first time and add the notification watcher:
plotSpace.scale(toFitEntirePlots: graph.allPlots())
graph.layoutSublayers()
enforceEqualAxes(xScale: 1.0, yScale: 1.0)
guard let plotArea = graph.plotAreaFrame?.plotArea else { return } NotificationCenter.default.addObserver(self, selector: #selector(self.didResizePlotArea), name: NSNotification.Name( CPTLayerNotification.boundsDidChange.rawValue), object: plotArea)