Core Plot and SwiftUI

170 views
Skip to first unread message

Jeffrey Terry

unread,
Dec 12, 2020, 7:36:56 AM12/12/20
to coreplot-discuss
Has anyone used Core Plot with SwiftUI? I may be looking in the wrong place but I did not see any SwiftUI examples. 

Thanks, 

Jeff

Eric

unread,
Dec 12, 2020, 7:04:25 PM12/12/20
to coreplot-discuss
I haven't seen any, either. Do you have a specific application in mind? 

Eric

Jeffrey Terry

unread,
Dec 13, 2020, 2:19:17 AM12/13/20
to coreplot-discuss
Well, that is a good question. The answer is a number of applications. 

A few years ago, we started teaching one of our Computational Physics courses using Swift. Our department chair bought a Mac lab for the students and said congrats Jeff, the class is yours. Learn Swift and teach it. Anyway, the first year, we used a plotting library that disappeared. The next year, I switched to Core Plot. An example video for the students is here. https://www.youtube.com/watch?v=2w8wFqudTN4&list=PLqZCvArs4yF8Hqs3-bgX5qKc0gOct0iVz&index=6&t=40s

For plotting, I just give them the basic code to modify. 

This year, the decision was made to drop the Cocoa interface for SwiftUI. Goal is to have all the students' code run on Macs, iPads, iPhones so they can be passed along as learning examples.

 So I have everything converted over for next semester's class except for plotting. I was hoping someone might have figured out how to use Core Plot with SwiftUI. I know it is possible to mix SwiftUI with UIKit or Cocoa but that is not what I'm looking for. 

I may have to just write some rudimentary plotting routine for this year. 

Thanks,

Jeff

JustFred

unread,
Dec 13, 2020, 4:53:04 AM12/13/20
to coreplot-discuss
I have not looked at Core Plot for at least 10 years (at least it feels like that :-) ). I have written a number of SwiftUI apps and have run into the problem that I wanted to use something that SwiftUI did not provide. In my case I wanted to use NSTableView.

SwiftUI does provide a means to integrate non SwiftUI components into the application. See e.g. Tutorial Apple which shows how to do this.

If someone can point me at a trivial Core Plot app I could make an attempt to create an integration into SwiftUI. Perhaps it is even better to create such a trivial Core Plot so that I can focus on the integration and not on how Core Plot works. My preference is a MacOS app.

Eric

unread,
Dec 13, 2020, 7:43:32 AM12/13/20
to coreplot-discuss
Have you looked in the Core Plot example apps? There are a bunch of examples of varying complexity. You could wrap the Core Plot hosting view inside SwiftUI and go from there.

I also found a Ray Wenderlich tutorial that shows how to build simple bar and line charts using nothing but SwiftUI components. For classroom purposes, this approach might be better.

Eric

Jeffrey Terry

unread,
Dec 13, 2020, 4:21:22 PM12/13/20
to coreplot-discuss
As we do fairly simple plotting, you can look at my example application for the students. It just plots e^-x. https://github.com/jterry94/CatalystPlot

Jeff

Jeffrey Terry

unread,
Dec 13, 2020, 4:24:06 PM12/13/20
to coreplot-discuss

I will probably write a simple plotting routine for class if I can't get CorePlot to work. The features of CorePlot, zooming, scrolling, real time plot updating though are really nice. Especially, when we are doing live data collection. 

JustFred

unread,
Dec 14, 2020, 8:48:02 AM12/14/20
to coreplot-discuss
I downloaded the code, watched the video and got it mostly working. With mostly I mean that it runs on an ipad simulator but it does not run on MacOS. Coreplot for MacOS compiles. When I build the CatalystPlot project for the Mac I it doesn't even start to build. I run into the warning:

Target Integrity: Multiple targets match implicit dependency for product reference etc.

I may try to build a simple example myself for MacOS only if nothting is availble.

JustFred

unread,
Dec 14, 2020, 11:27:38 AM12/14/20
to coreplot-discuss
Ok. I took the example DatePlot from the CorePlot library and got it working using SwiftUI.

Please give me a day or so to write this down properly. It is reasonably straight forward and should give a start for building other apps using SwiftUI.

Jeffrey Terry

unread,
Dec 14, 2020, 2:02:51 PM12/14/20
to coreplot-discuss
Can't wait to see it. 

JustFred

unread,
Dec 14, 2020, 2:26:31 PM12/14/20
to coreplot-discuss
It turned out to go quicker than expected. I have uploaded the code to github
and I have described the details in a post

I hope this helps someone. Some feedback would be appreciated.

Jeffrey Terry

unread,
Dec 14, 2020, 2:30:20 PM12/14/20
to coreplot-discuss
I will start taking a look. Greatly appreciate it. 

Jeffrey Terry

unread,
Dec 14, 2020, 7:56:31 PM12/14/20
to coreplot-discuss
Thank you. I definitely works on the Mac. I will try to figure out exactly what it is doing now ; )

Jeff

Jeffrey Terry

unread,
Dec 15, 2020, 10:37:51 PM12/15/20
to coreplot-discuss
Hi Fred, 

Thanks. I was able to modify the code to display a standard XY plot. I now just have to figure out how to make it replot when a calculation changes the data in the plot. I had hoped that using a binding to the data would do it but it wasn't that simple. 

Jeff

On Monday, December 14, 2020 at 1:26:31 PM UTC-6 JustFred wrote:

JustFred

unread,
Dec 16, 2020, 3:26:03 AM12/16/20
to coreplot-discuss
It should be that simple. When the data has changed, SwiftUI should call the method updateNSView() on your view.
In that view you should if possible do some check if the data has changed and then update the data for the view and call whatever method in CorePlot to update itself. (As I said I am not enough in the weeds of CorePlot to know how to update CorePlot, this is also why I left it out)

If you can check if the data has changed that would give a performance improvement by preventing unnecessary updates of the view.

Jeffrey Terry

unread,
Dec 16, 2020, 7:49:59 PM12/16/20
to coreplot-discuss
Hi Fred, 

Thanks for the help. I have tried everything that I can think of in updateNSView. It almost works but just does not. 
It only goes into the updateNSView when the data changes. That is actually pretty nice. If you recalculate but have the same values, it doesn't go into the upDataNSView. 

No matter what I do, I can't update the view. I even tried throwing in an animation to see if that would do it. Code I tried is below with many permutations commented out. 

Thanks for the try but I'm at a loss. I guess I will just have to write some simple plotting routine.

Jeff

    public func updateNSView(_ nsView: CPTGraphHostingView, context: Context) {

        

        print("in update")

        print(dataForPlot[0])

       // context.coordinator.parent.dataForPlot = dataForPlot

        

        let graph = nsView.hostedGraph

        let plot = graph?.plot(at: 0)

                

        guard let plotSpace = graph?.defaultPlotSpace as? CPTXYPlotSpace else { return }

        

        let oldRange =  CPTPlotRange(locationDecimal: CPTDecimalFromDouble(Double(0.0)), lengthDecimal: CPTDecimalFromDouble(Double(2.0)))

        let newRange =  CPTPlotRange(locationDecimal: CPTDecimalFromDouble(Double(0.0)), lengthDecimal: CPTDecimalFromDouble(Double(2.0)))

            

        CPTAnimation.animate(plotSpace, property: "xRange", from: oldRange, to: newRange, duration:0.3)

        plot?.insertData(at: UInt(0.0), numberOfRecords: UInt(dataForPlot.count))

        

     //   let newView = self.makeNSView(context: context)

     //   nsView.hostedGraph = newView.hostedGraph

        }


JustFred

unread,
Dec 17, 2020, 3:55:41 AM12/17/20
to coreplot-discuss
Hi Jeff,

For me it is difficult to pinpoint your problem. I can see two posssible future directions.
  1. You must implement a NSViewControllerRepresentable as well. It is very well possible that this is the missing link. It requires a better understanding on the internals of CorePlot to do that properly.
  2. Fall back on a trick. You can actually force a SwiftUI view to redraw itself by changing the 'id'. I have updated my example on github to show how this can be done. I have added the a button that changes the numbers and updates the view. Perhaps you can use that approach.

Jeffrey Terry

unread,
Dec 17, 2020, 8:11:40 AM12/17/20
to coreplot-discuss
Hi Fred, 

Ha, I used the id trick to get it to work by swapping out static views yesterday. That does unfortunately make is so live plot updating does not work. Having dug in deeper. The id trick changes the data of the parent while standard updating does not change the parent data. Not sure if that is a problem or a symptom. There is a vide of it working here with the id trick: https://twitter.com/nuclear94/status/1339392126455603202?s=21

I'll post all the code on GitHub. 

Not sure if the id trick is a huge memory leak or not. I don't know if all the different ids reside in memory or not. I did discover that you could not reuse the same id value. Anyway, it will work well enough for most of what I do in class. 

I will look into the NSViewControllerRepresentable. It would be really nice to get live updating of plot data to work so I will look into that as well. 

I greatly appreciate the help. 

Jeff

JustFred

unread,
Dec 17, 2020, 10:58:50 AM12/17/20
to coreplot-discuss
Hi Jeff,

I made one (probably last) attempt to get you going. My suspicion is that this has to do with reference versus value types.
I removed the 'id' from the hosting view and implemented updateNSView(). My example now updates the drawing if the data changes.
I hope you can do something similar in your app.

See github

Jeffrey Terry

unread,
Dec 17, 2020, 12:21:41 PM12/17/20
to coreplot-discuss
Hi Fred, 

I was unable to get the plot to refresh. 

If you have time to take a look at my code, it is on GitHub here: https://github.com/jterry94/SwiftUICorePlotexp--x-

The syntax is a little different between date plots and (x,y) data. It is possible that is the issue. 

Thanks,

Jeff

JustFred

unread,
Dec 17, 2020, 12:58:43 PM12/17/20
to coreplot-discuss
You missed one of my changes:

diff --git a/SwiftUICorePlot/CorePlot.swift b/SwiftUICorePlot/CorePlot.swift
index 3695c10..862c83e 100644
--- a/SwiftUICorePlot/CorePlot.swift
+++ b/SwiftUICorePlot/CorePlot.swift
@@ -170,7 +170,7 @@ public struct CorePlot: NSViewRepresentable {
         {
             let plotField = CPTScatterPlotField(rawValue: Int(field))

-            if let num = parent.dataForPlot[Int(record)][plotField!] {
+            if let num = data[Int(record)][plotField!] {
                 return num as NSNumber
             }
             else {

With that fix applied it does refresh.

Jeffrey Terry

unread,
Dec 17, 2020, 3:03:19 PM12/17/20
to coreplot-discuss
Yep, that was a big miss on my part. Makes perfect sense that that change was necessary. Thank you for all of your help. 

Jeff

JustFred

unread,
Dec 17, 2020, 3:19:07 PM12/17/20
to coreplot-discuss
No worries. I'm just happy to see that you have a have start to build from.

Jeffrey Terry

unread,
Dec 17, 2020, 4:06:09 PM12/17/20
to coreplot-discuss
Greatly appreciated all the assistance. 
Reply all
Reply to author
Forward
0 new messages