Modify the vertical label position for y-axis with ShinobiChart - ios

I have a ColumnSeries chart that I want to represent the majorTickLabel as if hovering above the majorGridLine but I don't see a straight forward way of controlling where the label draws beyond what I currently have which achieves insetting the label so that it 'hovers' above the graph.
Is this functionality missing from the API? If so, any suggestions on where to look at to achieve this?
Below is my current y-axis implementation
let yAxis = SChartNumberAxis()
yAxis.style.majorGridLineStyle.showMajorGridLines = true
yAxis.rangePaddingHigh = 500
yAxis.discontinuousTickLabelClipping = SChartDiscontinuousTickLabelClippingHigh
yAxis.axisPosition = SChartAxisPositionReverse
yAxis.style.majorTickStyle.tickGap = -45
goalChart.yAxis = yAxis
UPDATE- ANSWER BELOW
Thanks Sammy, simply offsetting the tickLabel resolved my issue. Below is a copy of the solution used.
#objc func sChart(chart: ShinobiChart!, alterTickMark tickMark: SChartTickMark!, beforeAddingToAxis axis: SChartAxis!) {
if !axis.isXAxis() {
if let label = tickMark.tickLabel {
label.frame.offset(dx: 0, dy: -8)
}
}
}

The tickGap property on SChartTickStyle gives you some control over the position of the tick mark label relative to the tick mark itself. It seems that you have discovered this, and that it doesn't offer the power that you require.
For more fine-grained control, you should look at implementing the SChartDelegate protocol, specifically the following method:
- (void)sChart:(ShinobiChart *)chart alterTickMark:(SChartTickMark *)tickMark
beforeAddingToAxis:(SChartAxis *)axis
This gives you an opportunity to change the tick mark before it gets added to the axis. You can get hold of the label via the tickLabel property. For further details on the other properties available to you during this method call, check out the documentation for SChartTickMark.
sam

Related

iOS Charts zoom into a range of values

Is it possible to set the chart zoom to a range of values within the dataset? So currently we have a chart that can display close to 100 values within it. What we're trying to achieve is the ability to zoom into a range of values i.e. The first 12 values within the dataset and then the user is able to scroll in either direction to see the remaining values within the chart.
We've currently tried using the setVisibleXRangeMaximum function but that only seems to display the first value within the chart rather than 12. And this may be because the data that we receive from he service can be unpredictable i.e. one value may be 1000 and the next maybe 100,000.
This is an extension that will let you zoom into a range of values like this:
extension BarLineChartViewBase {
func focusXRange(start: Double, end: Double) {
guard end > start else {
return
}
// Reset the zoom back to the full chart view, so that the setVisibleXRange call will zoom in to the desired range,
// setVisibleXRange only zooms in, not out.
// resetZoom() seems like it should be the right API call, but it doesn't work if the user is already zoomed
// in closer than the range to focus.
// zoomToCenter(scaleX: 1, scaleY: 1) similarly doesn't work
while !isFullyZoomedOut {
zoomOut()
}
let distance = end - start
// If the distance to focus on is greater than the xAxis maximum, we can't zoom in any further
guard distance < (xAxis.axisMaximum - xAxis.axisMinimum) else {
return
}
// Set the visible range to the distance between start and end. This will zoom the chart in to a range of that distance
setVisibleXRange(minXRange: xAxis.axisMinimum, maxXRange: xAxis.axisMinimum + distance)
// Move the chart to the start point
moveViewToX(start)
// Due to the previous setVisibleXRange call, the user cannot zoom back out.
// Restore the visible range to the full width of the dataset, so that the user can zoom back out.
// Note: this will not zoom the chart back out; setVisibleXRange will only zoom in, not out
setVisibleXRange(minXRange: xAxis.axisMinimum, maxXRange: xAxis.axisMaximum)
}
}
// Use it like
// chartView.focusXRange(0, 10) // Focus on the first ten data points
// chartView.focusXRange(chartView.xAxis.axisMaximum - 10, chartView.xAxis.axisMaximum) // Focus on the last ten data points

iOS: Programmatically move cursor up, down, left and right in UITextView

I use the following code to move the cursor position to 5 characters from the beginning of a UITextField:
txtView.selectedRange = NSMakeRange(5, 0);
Now, if I have a cursor at an arbitrary position as shown in the image below, how can I move the cursor up, down, left and right?
Left and right should be more or less easy. I guess the tricky part is top and bottom. I would try the following.
You can use caretRect method to find the current "frame" of the cursor:
if let cursorPosition = answerTextView.selectedTextRange?.start {
let caretPositionRect = answerTextView.caretRect(for: cursorPosition)
}
Then to go up or down, I would use that frame to calculate estimated position in UITextView coordinates using characterRange(at:) (or maybe closestPosition(to:)), e.g. for up:
let yMiddle = caretPositionRect.origin.y + (caretPositionRect.height / 2)
let lineHeight = answerTextView.font?.lineHeight ?? caretPositionRect.height // default to caretPositionRect.height
// x does not change
let estimatedUpPoint = CGPoint(x: caretPositionRect.origin.x, y: yMiddle - lineHeight)
if let newSelection = answerTextView.characterRange(at: estimatedUpPoint) {
// we have a new Range, lets just make sure it is not a selection
newSelection.end = newSelection.start
// and set it
answerTextView.selectedTextRange = newSelection
} else {
// I guess this happens if we go outside of the textView
}
I haven't really done it before, so take this just as a general direction that should work for you.
Documentation to the methods used is here.

How can I highlight the lowest data point in iOS-Charts?

I would like to put an image that indicates this data point is the lowest point in the data. I made a picture to explain what I want. I am using BarChartView and RadarChartView.
My trial
I think this question is similar to my question but I need to get position to put UIImage. If I know the position, I can do it or do addSubview. getMarkerPosition function looks useful to determine the right position.
Also, ChartMarker looks helpful. In the ChartsDemo some chart views are using BalloonMarker. It is almost what I want to do. Here is a quote from RadarChartViewController.m.
BalloonMarker *marker = [[BalloonMarker alloc] initWithColor:[UIColor colorWithWhite:180/255. alpha:1.0] font:[UIFont systemFontOfSize:12.0] insets: UIEdgeInsetsMake(8.0, 8.0, 20.0, 8.0)];
marker.minimumSize = CGSizeMake(80.f, 40.f);
_chartView.marker = marker;
BalloonMarker is created and added to _chartView.marker. Therefore, a marker comes up when an user clicks on a bar but I want to show a marker always. In other words, the library decides the right time to show and hide a marker . I couldn't find the way to pop up a marker actively.
I would be grateful if you could tell me the way to do this. Thank you.
In BarChartRenderer class you can find method
public override func drawValues(context context: CGContext)
inside body of if (!dataSet.isStacked) in the for-loop you can first define the minimum value using e.value and corresponding valuePoint variable, so after loop you will have coordinates where the minimum value will be drawn and adding some offset you can draw also your marker
let image = UIImage(named: "MARKER")
image?.drawInRect(CGRect(x: valuePoint.x, y: SOME_OFFSET + valuePoint.y + (val >= 0.0 ? posOffset : negOffset), width: 16, height: 16))
Or in
public func drawDataSet(context context: CGContext, dataSet:
IBarChartDataSet, index: Int)
You can find barRect of the smallest bar and play with that results.

change color of selected slice in Pie chart using iOS Charts

In the iOS Charts library, unlike the BarChartDataSet class, the PieChartDataSet does not contain any property highlightAlpha that can be used to set a different alpha to the selected slice on the pie chart.
Although such a property can be introduced and using CGContextSetAlpha() we can modify the transparency of the highlighted slice, I want to do it without making any change in the library code. How can it be done?
I checked the code, currently, it does not support this.
public override func drawHighlighted(context context: CGContext, indices: [ChartHighlight])
{
...
CGContextSetFillColorWithColor(context, set.colorAt(xIndex).CGColor)
...
}
This just read data set color and use it to highlight. I think you are welcome to file a PR for such feature. Or I will do it when I have time.
For you, changing the source code seems the only option right now. That's why I think it's good for you to contribute.
For now, I have solved the problem using the delegate method:
- (void)chartValueSelected:(ChartViewBase * __nonnull)chartView
entry:(ChartDataEntry * __nonnull)entry
dataSetIndex:(NSInteger)dataSetIndex
highlight:(ChartHighlight * __nonnull)highlight
{
PieChartView *pPieChartView = (PieChartView *)chartView;
PieChartDataSet *pDataSet = (PieChartDataSet *)[pPieChartView.data.dataSets objectAtIndex:dataSetIndex];
NSMutableArray *pColors = [[NSMutableArray alloc] initWithArray:pDataSet.colors
copyItems:YES];
for (int nIndex = 0; nIndex < pColors.count; nIndex++) {
UIColor *pColor = [pColors objectAtIndex:nIndex];
if (nIndex == entry.xIndex) {
pColor = [pColor colorWithAlphaComponent:1];
}
else {
pColor = [pColor colorWithAlphaComponent:0.3];
}
[pColors replaceObjectAtIndex:nIndex
withObject:pColor];
}
pDataSet.colors = pColors;
}
In my case, I load the slices with alpha component less than 1. On highlighting the slice, the alpha value is changed to 1.
The same effect can be achieved if the highlightAlpha property is introduced in PieChartDataSet class. In the drawHighlighted method, CGContextSetAlpha(context, highlightAlpha) needs to be called. The BarChartDataSet has highlight colors as well, which are absent in PieChartDataSet.

Caret position relative to screen coordinates in javaFX

I am trying to find a way to get the corresponding screen location of the caret-position in a text area in JavaFX. I need the location to show Popups in text at the caret location.
I found request or it here:
https://bugs.openjdk.java.net/browse/JDK-8090849
and some workarounds here:
https://community.oracle.com/thread/2534556
They work somehow, but there are a few issues with location not updating correctly sometimes. Does anyone have a suggestion of how to get the caret position in terms of screen X and Y?
Just wanted to follow-up with an answer to this question for TextField controls in JavaFX. I'm sure the same could apply to other text input controls as well. I got the idea from looking at some code that involved changing the default colour of the caret using a subclass of TextFieldSkin class. If you look closely, the TextFieldSkin superclass holds a reference to the Path instance which represents the caret in a protected field called caretPath. Although this is kind of a hack'ish solution, it does provide developers with the absolute coordinates of the Caret in a much safer way than most of the hacks I've seen out there.
public class TextFieldCaretControlSkin extends TextFieldSkin {
public TextFieldCaretControlSkin(TextField textField, Stage stage) {
super(textField);
Popup popup = new Popup();
// Make the popup appear to the right of the caret
popup.setAnchorLocation(PopupWindow.AnchorLocation.CONTENT_BOTTOM_LEFT);
// Make sure its position gets corrected to stay on screen if we go out of screen
popup.setAutoFix(true);
// Add list view (mock autocomplete popup)
popup.getContent().add(new ListView<String>());
// listen for changes in the layout bounds of the caret path
caretPath.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> observable,
Bounds oldValue, Bounds newValue) {
popup.hide();
// get the caret's x position relative to the textfield.
double x = newValue.getMaxX();
// get the caret's y position relative to the textfield.
double y = newValue.getMaxY();
Point2D p = caretPath.localToScene(x, y);
/*
* If the coordinates are negatives then the Path is being
* redrawn and we should just skip further processing.
*/
if (x == -1.0 || y == -1.0)
return;
// show the popup at these absolute coordinates.
popup.show(textField,
p.getX() + caretPath.getScene().getX() +
caretPath.getScene().getWindow().getX(),
p.getY() + caretPath.getScene().getY() +
caretPath.getScene().getWindow().getY() -
newValue.getHeight()); // put the popup on top of the caret
}
});
}
}
To use you'd have to embed this in some sort of subclassed text input control and remember to do textField.setSkin(new TextFieldCaretControlSkin(textField)). There may be better ways to do this since I am not a JavaFX expert but I just wanted to share this solution with the rest of the world just in case it provided some insight.
Hope this helps!
This is how you use RichTextFX to position a popup window 4px to the right of the caret:
InlineCssTextArea area = new InlineCssTextArea();
Popup popup = new Popup();
popup.getContent().add(new Label("I am a popup label!"));
area.setPopupWindow(popup);
area.setPopupAlignment(PopupAlignment.CARET_CENTER);
area.setPopupAnchorOffset(new Point2D(4, 0));
You still need to control the visibility of the popup window yourself by calling
popup.show(ownerWindow);
popup.hide();
See also this working demo.

Resources