-
-
Notifications
You must be signed in to change notification settings - Fork 6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update the renderers with dedicated atomic rendering methods in order to make rendering overrides easier to implement. #4297
Conversation
… to make rendering overrides easier to implement.
Codecov Report
@@ Coverage Diff @@
## master #4297 +/- ##
==========================================
+ Coverage 41.8% 41.83% +0.02%
==========================================
Files 124 124
Lines 14117 14173 +56
==========================================
+ Hits 5902 5929 +27
- Misses 8215 8244 +29
Continue to review full report at Codecov.
|
I'm all for this, but there has been resistance to adding more function calls in the past. I believe the justification for this hasn't been valid since about Swift 3, but as a result @danielgindi will have to decide. |
Thank you for your feedback and thoughts @jjatie 👍 |
@danielgindi @liuxuan30 @jjatie : do you have any thoughts about adding these override points for rendering logic? |
# Conflicts: # Source/Charts/Renderers/BarChartRenderer.swift # Source/Charts/Renderers/LegendRenderer.swift # Source/Charts/Renderers/LineChartRenderer.swift # Source/Charts/Renderers/PieChartRenderer.swift
@danielgindi @liuxuan30 : I updated the PR to the latest revision from Perform these three steps to the demo application in order to reproduce the example in my screenshots below:
/// Custom bar chart view that fills bars with a circular pattern.
class PatternFilledBarChartView: BarChartView {
override init(frame: CGRect) {
super.init(frame: frame)
updateRenderer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateRenderer()
}
private func updateRenderer() {
let chartAnimator = renderer?.animator ?? Animator()
renderer = PatternFilledBarChartRenderer(dataProvider: self,
animator: chartAnimator,
viewPortHandler: viewPortHandler)
}
}
/// Custom bar chart renderer that fills bars with a circular pattern.
class PatternFilledBarChartRenderer: BarChartRenderer {
override func renderFill(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: BarChartDataSetProtocol, entry: BarChartDataEntry?) {
let patternSize = CGSize(width: 10, height: 10)
let patternCallback: CGPatternDrawPatternCallback = { (pointer, context) -> Void in
context.addArc(center: CGPoint(x: 5, y: 5), radius: 5, startAngle: 0, endAngle: CGFloat(2.0 * .pi), clockwise: true)
context.fillPath()
}
var callbacks = CGPatternCallbacks(version: 0, drawPattern: patternCallback, releaseInfo: nil)
let pattern = CGPattern(info: nil,
bounds: CGRect(origin: CGPoint(x: 0, y: 0), size: patternSize),
matrix: .identity,
xStep: patternSize.width,
yStep: patternSize.height,
tiling: .constantSpacing,
isColored: false,
callbacks: &callbacks)
let patternColorSpace = CGColorSpace(patternBaseSpace: CGColorSpaceCreateDeviceRGB())
let colorComponents = UnsafeMutablePointer<CGFloat>.allocate(capacity: 4)
color.getRed(&colorComponents[0], green: &colorComponents[1], blue: &colorComponents[2], alpha: &colorComponents[3])
defer {
colorComponents.deinitialize(count: 4)
colorComponents.deallocate()
}
context.saveGState()
context.setFillColorSpace(patternColorSpace!) //swiftlint:disable:this force_unwrapping
context.setFillPattern(pattern!, colorComponents: colorComponents) //swiftlint:disable:this force_unwrapping
context.fill(rect)
context.restoreGState()
}
} Screenshots |
Reverted in 7e1f6a0 as it caused the tests to fail and it deviated from the focussed PR. |
@danielgindi @liuxuan30 @jjatie : just a quick look at open issues and PRs (see for example these ☝🏻), it looks like this PR will solve quite a number of them. Rather than extending the charting library with specific logic for very specific use cases, this PR allows people to create their custom renderers and override rendering to fit their specific needs. |
The only issue I see here is a performance question. Does it change with this implementation and how much if yes. Other than that this is a perfect solution in many-many cases. I would say it should be done for all renderers (or should have such option) because recently had to modify xAxis one to match designer idea of the beauty :) |
@bivant : It shouldn't really, as it is only introducing dedicated open overridable |
@danielgindi @liuxuan30 @jjatie: anybody? |
@danielgindi @liuxuan30 @jjatie: bump... |
When this will be merged? |
@jjatie what you think for this PR now? |
I had a quick look and it seems great, if we don't consider the android part; unfortunately we need to think about iOS and android interface again :( |
I think that as Charts is today, the implementation makes sense. Two notes:
If we agree that we should move forward with this, we need to audit for inline-ability. We also need to ensure all |
@liuxuan30 : In essence the public Charts API does not change, it merely adds ways to more easily customize rendering behavior by introducing a couple of focussed open methods. Perhaps @jjatie : Yes true, you don't need to subclass May I suggest that this perhaps does not merge to ps. |
@danielgindi please take a look at this PR |
I think this is something that I would actually want to port Android too. Will try to dedicate some time soon |
Quick question, how do I apply the same logic to achieve a piechart with rounded corners? I've been really trying it for a long time without success. Any help will really be much appreciated. Thanks a lot. |
@kelvinofula do not hijack. @jjatie I think we reach an agreement with @danielgindi that, we could go ahead to make some iOS features and refactorings. |
Recently I have been working on a project, to which I have added a chart library and display a beautiful rounded bar chart in swift. We can achieve this functionality easily. Please check this demo. |
@AppCodeZip : Do not hijack this Pull Request, your example is exactly opposite of what this PR is about. |
@jjatie I'm sorry to say that, I got a layoff and find a new job now, so I would be super busy for the rest of the year, tons of pressure from corp projects.. I might be missing for a few months (and already) If you think the PR is good to go, probably you want to go ahead without my review. |
Ouch @liuxuan30, I am sorry to hear that. I can imagine your mind is not with this repository and pull request. Best of luck with your job hunt. Perhaps iOS Dev Jobs might be useful to you? |
@4np haha thank you. I think my new job is lovely and challenging, especialy there are 6 months trial period for me, so I have to spend all my time on that. I really wanted to spend more time on Charts but recently I guess I couldn't afford. So I have to stay quiet for some time. I trust @jjatie and @danielgindi could take care of some PRs. |
@pmairoldi @liuxuan30 @jjatie @danielgindi Could you please take another look at this PR? :) Right now we have to maintain a fork to make it easier to override rendering, but it also makes uptaking latest changes more difficult. It would be great to have this merge into the repository so we can get rid of the fork. Like mentioned above, it might solve a lot of other issues or, at the very least, make it easier for other developers to implement rendering changes. |
@4np @pmairoldi @liuxuan30 @jjatie @danielgindi |
@pmairoldi @liuxuan30 @jjatie @danielgindi This PR has been open for close to 3 years. I would still like to see this PR getting merged though. Any updates? |
We just celebrated the 4th anniversary of this PR! 🥳🎊 As we have since migrated over to Swift Charts and it doesn't look like this PR will ever be merged, I am hereby closing it. |
Context
Often when implementing charting features, there is a requirement to implement certain UI changes. The more simple changes can often be accomplished by subclassing a particular renderer and overriding some of its methods, but more elaborate changes can be hard to accomplish.
While many methods are marked with
open
visibility and can theoretically be overloaded, in practice this is not as straightforward as most of those methods either call other methods or use variables that haveprivate
orinternal
visibility.Consider the use case where you want to fill a bar chart with a pattern instead of a color. In order to do so, you would need to subclass the
BarChartRenderer
and overridedrawDataSet(context:, dataSet:, index:)
(ordrawHighlighted(context:, indices:)
to update the highlight rendering).There are a couple of challenges with this approach:
drawDataSet(context:, dataSet:, index:)
performs quite some calculations you really do not want to replicate and want to keep 'in the charting framework' (separation of concerns).internal
orprivate
visibility, which you cannot call from your subclassed renderer (for exampleprepareBuffer(dataSet:, index:)
orBarLineScatterCandleBubbleRenderer.isInBoundsX(entry:, dataSet:)
), which in turn requires you to duplicate those implementation as well.internal
orprivate
visibility (e.g._barShadowRectBuffer
,_buffers
, etcetera), again requiring unnecessary code duplications.Charts
version is cumbersome and requires diffing duplicate logic for changes.Solution
This Pull Request lifts the rendering logic out of the methods into their own dedicated methods with
open
visibility. This way it becomes really easy to create a custom renderer that implements custom rendering behavior, without the need of having to duplicate complex logic or re-implementprivate
/internal
methods and/or variables. It allows you to focus on what matters, while keeping your subclassed renderer focussed and simple.Example: Bar Chart Pattern Fill.
This is an example of creating a custom bar chart renderer that will fill bars with a circular pattern:
Implementing this rendering override can now be done by overriding a single method in a straightforward manner, whereas before you needed to copy over pretty much the complete implementation of
BarChartRenderer.swift
(and more) while making small alterations to accomplish the same.Screenshot:
Lastly
It would be greatly appreciated if this could make it into a newly drafted release in the not so distant future 🙏🏻 😉