Adding and removing NSTextField programatically in SWIFT OS X - macos

I am trying to write text labels on a graph view representing a variety of data points (imagine a line graph in Excel).
I select a data set using a tableView. Each time a select a new data set the graph is redrawn.
I can draw the line graph using a custom view. No problems.
I can draw the text labels using the following function called from drawRect():
func drawDataPointLabels(size: CGSize) { //size is the view.bounds
var dataLabelsArray: [(NSColor, String, NSRect)] = []
// graphDetails contain an array of tuples [(), (), (), ()] - this represents one graph type = eg Monthly Fees ** there will be a maximum of 12 items in the array
for graphDetails in graphDetailsArray {
// each dataPoint object is a tuple (graphType: columnLabel: columnColor: columnHeight: columnNumber: ) representing one dataPoint
for dataPoint in graphDetails {
let graphHeight = size.height / 2
let graphWidth = size.width - graphOrigin.x - rightMargin
let columnAndGapSize = graphWidth / 25
let labelX: CGFloat = (graphOrigin.x + columnAndGapSize) + (columnAndGapSize * dataPoint.columnNumber) * 2
let labelY: CGFloat = dataPoint.columnHeight * (graphHeight - topMargin - bottomMargin)
// determine the location and frame for the text label to match dataPoint
let textFrame = NSRect(x: labelX, y: labelY, width: 30, height: 10)
let dataPointTuple = (dataPoint.columnColor, dataPoint.columnLabel, textFrame)
dataLabelsArray.append(dataPointTuple)
}
for dataPoint in dataLabelsArray {
let (color, label, frameRect) = dataPoint
let lblDataPoint = NSTextField(frame: frameRect)
lblDataPoint.stringValue = label
lblDataPoint.backgroundColor = backgroundColor
lblDataPoint.textColor = color
self.addSubview(lblDataPoint)
}
}
}
However, I can not work out how to remove the data labels when the view is updated and a new data set presented. The graph redraws but the text labels remain from the previous data set.
Any guidance/suggestions would be appreciated.

Adding the following code to the top of the function has solved the issue for me.
let subViewsToRemove = self.subviews
for object in subViewsToRemove {
object.removeFromSuperview()
}

Related

How to extract pixel value from geometry points in shp format (2500 points) starting from an Image Collection resempled

I'm trying to extract the value of each single band of Sentinel-2 after resampling the bands from 10 meters to 30 meters using the resolution of the Landsat-8 (I didn't know how to do this in another way)
This is the code using band 4 as an example. When I try to export the values ​​in a table the running time is infinite and I was not able to extract even the value for a single point.
N.B. my area is very large, it corresponds to all of southern europe
thank you !
function maskS2clouds(image) {
var qa = image.select('QA60');
var cloudBitMask = 1 << 10;
var cirrusBitMask = 1 << 11;
var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(qa.bitwiseAnd(cirrusBitMask).eq(0));
return image.updateMask(mask).divide(10000).select("B.*").copyProperties(image, ["system:time_start"]);
}
var start = '2012-01-01';
var end = '2022-12-31';
var cld_max = 30;
var s2 = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
.filterBounds(Eur)
.filterDate(start,end)
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE',60))
.map(maskS2clouds);
var land8 = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR').filterBounds(Eur)
var b4 = land8.select('B4').filterBounds(Eur); // 30-m resolution
var b4s = s2.select('B4').filterBounds(Eur); // 10-m resolution
var proj_10m = b4.first()
.projection();
function resample(image) {
var b4s = image.select('B4');
var b4s_res = b4s.resample('bilinear').reproject(proj_10m);
return b4s_res;
}
var b4s_res =b4s.map(resample).filterBounds(Eur);
//original code
var pts = b4s_res.map(function(img) {
var obs = img.reduceRegion(
{geometry: points , reducer: ee.Reducer.median(), scale: 80});
return img.set('B4', obs.get('B4'));
});
Export.table.toDrive({
collection: pts,
description: 'table_csv',
folder: 'earth_engine_demos',
fileFormat: 'CSV'
});
//test with geometry composed of two single points
var pts2 = b4s_res.map(function(img) {
var obs2 = img.reduceRegion(
{geometry: geometry , reducer: ee.Reducer.median(), scale: 80});
return img.set('B4', obs2.get('B4'));
});
Export.table.toDrive({
collection: pts2,
description:'prova_csv',
folder: 'earth_engine_demos',
fileFormat: 'CSV'
});
Is it possible to find a faster way to extract the value of the points (2500 random points) in the table? Do you also know another way to apply a resampling on all bands simultaneously and extract the corresponding value of each point for each single band?

amCharts Custom legend with values (correct method and alignment)

I'm currently building a heat map, with a 100+ data points (Country, value).
I've created a custom legend to only display the top 10 countries and their values.
However the alignment is off (wrong side and the distance between the name and values), is there a better way to do this?
chart.legend = new am4maps.Legend();
chart.legend.position = "right";
chart.legend.data = [];
for (let index = 0; index < 10; index++) {
chart.legend.data.push({
name: legend_data[index]['id'],
fill: colorSet.next(),
value: legend_data[index]['value']
});
}
chart.legend.labels.template.text = "{name}: {value}%";
chart.legend.itemContainers.template.paddingTop = 2;
chart.legend.itemContainers.template.paddingBottom = 2;
Very simple answer, I just needed to set the valueLabels text, rather than concatenating both into the legend label text. Aligns correctly now.
chart.legend.labels.template.text = "{name}";
chart.legend.valueLabels.template.text = "{value}";

Change subview layer position

Good evening,
I have been trying to figure out how to fade a screen when I press a button in order to give a subview a more distinct appearance. I have researched zPositions and I thought that implementing this feature within my code would help. When I run my code, the entire screen fades. I would like there to be a circle progress ring that shows up, while the background is faded. I will show a picture and my code. Thank you in advance.
#objc func handlePost() {
let center = view.center
let trackLayer = CAShapeLayer()
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
blackScreen=UIView(frame: self.view.bounds)
blackScreen.backgroundColor=UIColor(white: 0, alpha: 0.8)
blackScreen.isHidden=true
self.navigationController?.view.addSubview(blackScreen)
blackScreen.layer.zPosition=50
blackScreen.isHidden=false
self.view.backgroundColor = UIColor.white
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 10
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = kCALineCapRound
// The zPosition is supposed to bring this layer to the front.
trackLayer.zPosition = 100
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = GREEN_Theme.cgColor
shapeLayer.lineWidth = 10
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = kCALineCapRound
shapeLayer.strokeEnd = 0
// The zPosition is supposed to bring this layer to the front.
shapeLayer.zPosition = 100
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = 1
basicAnimation.duration = 2
basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false
shapeLayer.add(basicAnimation, forKey: "dataSearch")
shapeLayer.removeAnimation(forKey: "finished")
view.layer.addSublayer(trackLayer)
view.layer.addSublayer(shapeLayer)
}
Again after spending hours with my code, I managed to figure out my own answer.
before I had
self.navigationController?.view.addSubview(blackScreen)
// (faded screen)
I simply needed to add
self.view.addSubview(blackScreen)
to turn it into a layer that way I could manipulate it with my zPosition.

CGWindowListCreateImage yields blurred cgImage when zoomed

I'm developing a magnifying glass like application for mac. My goal is to be able to pinpoint individual pixels when zoomed in. I'm using this code in mouseMoved(with event: NSEvent):
let captureSize = self.frame.size.width / 9 //9 is the scale factor
let screenFrame = (NSScreen.main()?.frame)!
let x = floor(point.x) - floor(captureSize / 2)
let y = screenFrame.size.height - floor(point.y) - floor(captureSize / 2)
let windowID = CGWindowID(self.windowNumber)
cgImageExample = CGWindowListCreateImage(CGRect(x: x, y: y, width: captureSize,
height: captureSize), CGWindowListOption.optionOnScreenBelowWindow, windowID,
CGWindowImageOption.bestResolution)
The creation of the cgImage takes place in the CGWindowListCreateImage method. When I later draw this in an NSView, the result looks like this:
It looks blurred / like some anti-aliasing was applied during the creation of the cgImage. My goal is to get a razor sharp representation of each pixel. Can anyone point me in the right direction?
Ok, I figured it out. It was a matter of setting the interpolation quality to none on the drawing context:
context.interpolationQuality = .none
Result:
On request some more code:
//get the context
guard let context = NSGraphicsContext.current()?.cgContext else { return }
//get the CGImage
let image: CGImage = //pass the result from CGWindowListCreateImage call
//draw
context.draw(image, in: (CGRect of choice))

Extending ScrollView according to the information inside and saving?

I am trying to create a ScrollView that grows according to the amount of information added to it and save the data. How can I accomplish this? I have been trying to figure this out for a while and am unsure of how to do it. The data that I want to save is inside a TextField. I am unsure if this could cause any problems.
Any input and suggestions would be greatly appreciated.
Thanks in advance.
You can use the .contentSize of a UIScrollView to set the content size.
If you want to base the content size of the size of the information, you could expand the content size every time you add new content
var scrollView:UIScrollView = UIScrollView(frame: CGRectMake(0, 0, 100, 200))
scrollView.contentSize = CGSizeMake(100, 800)
var textField:UITextField = UITextField(frame: CGRectMake(0, scrollView.frame.height, scrollView.frame.width, 200))
scrollView.contentSize = CGSizeMake(scrollView.contentSize.width, scrollView.contentSize.height + textField.frame.height)
scrollView.addSubview(textField)
textField.text = "Foo"
var lbls = [String]()
for view in scrollView.subviews{
if let view:UITextField = view as? UITextField{
var lbl = view.text
}
}
//Save lbls as array or every string inside lbls using core data
Here I set the height of the content size to the height of my label + the current height of the content size.

Resources