How do I find an appropriate position for my labels on a pie chart?











up vote
2
down vote

favorite












I am not using any libraries so this is not a duplicate of this.



I am drawing the pie sections myself like this:



var sections: [PieChartSection] =  {
didSet {
setNeedsDisplay()
}
}

override func draw(_ rect: CGRect) {
let sumOfSections = sections.map { $0.value }.reduce(0, +)
var pathStart = CGFloat.pi
let smallerDimension = min(height, width)
for section in sections {
// draw section
let percentage = section.value / sumOfSections
let pathEnd = pathStart + CGFloat.pi * percentage.f * 2
let path = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
radius: smallerDimension / 4, startAngle: pathStart, endAngle: pathEnd, clockwise: true)

//draw labels
// this is my attempt at calculating the position of the labels
let midAngle = (pathStart + pathEnd) / 2
let textX = bounds.midX + smallerDimension * 3 / 8 * cos(midAngle)
let textY = bounds.midY + smallerDimension * 3 / 8 * sin(midAngle)

// creating the text to be shown, don't this is relevant
let attributedString = NSMutableAttributedString(string: section.name, attributes: [
.foregroundColor: UIColor.black.withAlphaComponent(0.15),
.font: UIFont.systemFont(ofSize: 9)
])
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 0
let percentageString = "n" + formatter.string(from: (percentage * 100) as NSNumber)! + "%"
attributedString.append(NSAttributedString(string: percentageString, attributes: [
.foregroundColor: UIColor.black.withAlphaComponent(0.5),
.font: UIFont.systemFont(ofSize: 12)
]))
attributedString.draw(at: CGPoint(x: textX, y: textY))

// stroke path
path.lineWidth = 6
section.color.setStroke()
path.stroke()
pathStart = pathEnd
}
}


And a PieChartSection is a simple struct:



struct PieChartSection {
let value: Double
let color: UIColor
let name: String
}


The pie looks good, but the labels are sometimes far away from the pie and sometimes very close to it:



enter image description here



I think the problem is that NSAttriutedString.draw always draws the text from the top left corner, meaning that the top left corners of the text are all equal-distance to the pie, whereas I need to draw the text so that their closest points to the pie are all equal-distance to the pie.



How can I draw text like that?



I am not using a cocoa pod because I find it very hard to make the chart the way I want using a high-level API. There is simply too much complexity involved. I want lower-level control over how my pie chart is drawn.










share|improve this question


























    up vote
    2
    down vote

    favorite












    I am not using any libraries so this is not a duplicate of this.



    I am drawing the pie sections myself like this:



    var sections: [PieChartSection] =  {
    didSet {
    setNeedsDisplay()
    }
    }

    override func draw(_ rect: CGRect) {
    let sumOfSections = sections.map { $0.value }.reduce(0, +)
    var pathStart = CGFloat.pi
    let smallerDimension = min(height, width)
    for section in sections {
    // draw section
    let percentage = section.value / sumOfSections
    let pathEnd = pathStart + CGFloat.pi * percentage.f * 2
    let path = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
    radius: smallerDimension / 4, startAngle: pathStart, endAngle: pathEnd, clockwise: true)

    //draw labels
    // this is my attempt at calculating the position of the labels
    let midAngle = (pathStart + pathEnd) / 2
    let textX = bounds.midX + smallerDimension * 3 / 8 * cos(midAngle)
    let textY = bounds.midY + smallerDimension * 3 / 8 * sin(midAngle)

    // creating the text to be shown, don't this is relevant
    let attributedString = NSMutableAttributedString(string: section.name, attributes: [
    .foregroundColor: UIColor.black.withAlphaComponent(0.15),
    .font: UIFont.systemFont(ofSize: 9)
    ])
    let formatter = NumberFormatter()
    formatter.maximumFractionDigits = 0
    let percentageString = "n" + formatter.string(from: (percentage * 100) as NSNumber)! + "%"
    attributedString.append(NSAttributedString(string: percentageString, attributes: [
    .foregroundColor: UIColor.black.withAlphaComponent(0.5),
    .font: UIFont.systemFont(ofSize: 12)
    ]))
    attributedString.draw(at: CGPoint(x: textX, y: textY))

    // stroke path
    path.lineWidth = 6
    section.color.setStroke()
    path.stroke()
    pathStart = pathEnd
    }
    }


    And a PieChartSection is a simple struct:



    struct PieChartSection {
    let value: Double
    let color: UIColor
    let name: String
    }


    The pie looks good, but the labels are sometimes far away from the pie and sometimes very close to it:



    enter image description here



    I think the problem is that NSAttriutedString.draw always draws the text from the top left corner, meaning that the top left corners of the text are all equal-distance to the pie, whereas I need to draw the text so that their closest points to the pie are all equal-distance to the pie.



    How can I draw text like that?



    I am not using a cocoa pod because I find it very hard to make the chart the way I want using a high-level API. There is simply too much complexity involved. I want lower-level control over how my pie chart is drawn.










    share|improve this question
























      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      I am not using any libraries so this is not a duplicate of this.



      I am drawing the pie sections myself like this:



      var sections: [PieChartSection] =  {
      didSet {
      setNeedsDisplay()
      }
      }

      override func draw(_ rect: CGRect) {
      let sumOfSections = sections.map { $0.value }.reduce(0, +)
      var pathStart = CGFloat.pi
      let smallerDimension = min(height, width)
      for section in sections {
      // draw section
      let percentage = section.value / sumOfSections
      let pathEnd = pathStart + CGFloat.pi * percentage.f * 2
      let path = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
      radius: smallerDimension / 4, startAngle: pathStart, endAngle: pathEnd, clockwise: true)

      //draw labels
      // this is my attempt at calculating the position of the labels
      let midAngle = (pathStart + pathEnd) / 2
      let textX = bounds.midX + smallerDimension * 3 / 8 * cos(midAngle)
      let textY = bounds.midY + smallerDimension * 3 / 8 * sin(midAngle)

      // creating the text to be shown, don't this is relevant
      let attributedString = NSMutableAttributedString(string: section.name, attributes: [
      .foregroundColor: UIColor.black.withAlphaComponent(0.15),
      .font: UIFont.systemFont(ofSize: 9)
      ])
      let formatter = NumberFormatter()
      formatter.maximumFractionDigits = 0
      let percentageString = "n" + formatter.string(from: (percentage * 100) as NSNumber)! + "%"
      attributedString.append(NSAttributedString(string: percentageString, attributes: [
      .foregroundColor: UIColor.black.withAlphaComponent(0.5),
      .font: UIFont.systemFont(ofSize: 12)
      ]))
      attributedString.draw(at: CGPoint(x: textX, y: textY))

      // stroke path
      path.lineWidth = 6
      section.color.setStroke()
      path.stroke()
      pathStart = pathEnd
      }
      }


      And a PieChartSection is a simple struct:



      struct PieChartSection {
      let value: Double
      let color: UIColor
      let name: String
      }


      The pie looks good, but the labels are sometimes far away from the pie and sometimes very close to it:



      enter image description here



      I think the problem is that NSAttriutedString.draw always draws the text from the top left corner, meaning that the top left corners of the text are all equal-distance to the pie, whereas I need to draw the text so that their closest points to the pie are all equal-distance to the pie.



      How can I draw text like that?



      I am not using a cocoa pod because I find it very hard to make the chart the way I want using a high-level API. There is simply too much complexity involved. I want lower-level control over how my pie chart is drawn.










      share|improve this question













      I am not using any libraries so this is not a duplicate of this.



      I am drawing the pie sections myself like this:



      var sections: [PieChartSection] =  {
      didSet {
      setNeedsDisplay()
      }
      }

      override func draw(_ rect: CGRect) {
      let sumOfSections = sections.map { $0.value }.reduce(0, +)
      var pathStart = CGFloat.pi
      let smallerDimension = min(height, width)
      for section in sections {
      // draw section
      let percentage = section.value / sumOfSections
      let pathEnd = pathStart + CGFloat.pi * percentage.f * 2
      let path = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
      radius: smallerDimension / 4, startAngle: pathStart, endAngle: pathEnd, clockwise: true)

      //draw labels
      // this is my attempt at calculating the position of the labels
      let midAngle = (pathStart + pathEnd) / 2
      let textX = bounds.midX + smallerDimension * 3 / 8 * cos(midAngle)
      let textY = bounds.midY + smallerDimension * 3 / 8 * sin(midAngle)

      // creating the text to be shown, don't this is relevant
      let attributedString = NSMutableAttributedString(string: section.name, attributes: [
      .foregroundColor: UIColor.black.withAlphaComponent(0.15),
      .font: UIFont.systemFont(ofSize: 9)
      ])
      let formatter = NumberFormatter()
      formatter.maximumFractionDigits = 0
      let percentageString = "n" + formatter.string(from: (percentage * 100) as NSNumber)! + "%"
      attributedString.append(NSAttributedString(string: percentageString, attributes: [
      .foregroundColor: UIColor.black.withAlphaComponent(0.5),
      .font: UIFont.systemFont(ofSize: 12)
      ]))
      attributedString.draw(at: CGPoint(x: textX, y: textY))

      // stroke path
      path.lineWidth = 6
      section.color.setStroke()
      path.stroke()
      pathStart = pathEnd
      }
      }


      And a PieChartSection is a simple struct:



      struct PieChartSection {
      let value: Double
      let color: UIColor
      let name: String
      }


      The pie looks good, but the labels are sometimes far away from the pie and sometimes very close to it:



      enter image description here



      I think the problem is that NSAttriutedString.draw always draws the text from the top left corner, meaning that the top left corners of the text are all equal-distance to the pie, whereas I need to draw the text so that their closest points to the pie are all equal-distance to the pie.



      How can I draw text like that?



      I am not using a cocoa pod because I find it very hard to make the chart the way I want using a high-level API. There is simply too much complexity involved. I want lower-level control over how my pie chart is drawn.







      ios swift charts drawing






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 17 at 12:37









      Sweeper

      60.6k967134




      60.6k967134
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          4
          down vote



          accepted











          I think the problem is that NSAttriutedString.draw always draws the text from the top left corner




          Sure, the draw method will place origin of the string right in the point you pass. In this case solution is simple - find the size of string and make correct origin.



          let size = attributedString.size()
          let origin = CGPoint(x: textX - size.width / 2, y: textY - size.height / 2)
          attributedString.draw(at: origin)


          Result:



          enter image description here






          share|improve this answer

















          • 1




            Thank you! It worked! I never thought it was this simple! I guess I'm just bad at maths...
            – Sweeper
            Nov 17 at 15:37











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53351327%2fhow-do-i-find-an-appropriate-position-for-my-labels-on-a-pie-chart%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          4
          down vote



          accepted











          I think the problem is that NSAttriutedString.draw always draws the text from the top left corner




          Sure, the draw method will place origin of the string right in the point you pass. In this case solution is simple - find the size of string and make correct origin.



          let size = attributedString.size()
          let origin = CGPoint(x: textX - size.width / 2, y: textY - size.height / 2)
          attributedString.draw(at: origin)


          Result:



          enter image description here






          share|improve this answer

















          • 1




            Thank you! It worked! I never thought it was this simple! I guess I'm just bad at maths...
            – Sweeper
            Nov 17 at 15:37















          up vote
          4
          down vote



          accepted











          I think the problem is that NSAttriutedString.draw always draws the text from the top left corner




          Sure, the draw method will place origin of the string right in the point you pass. In this case solution is simple - find the size of string and make correct origin.



          let size = attributedString.size()
          let origin = CGPoint(x: textX - size.width / 2, y: textY - size.height / 2)
          attributedString.draw(at: origin)


          Result:



          enter image description here






          share|improve this answer

















          • 1




            Thank you! It worked! I never thought it was this simple! I guess I'm just bad at maths...
            – Sweeper
            Nov 17 at 15:37













          up vote
          4
          down vote



          accepted







          up vote
          4
          down vote



          accepted







          I think the problem is that NSAttriutedString.draw always draws the text from the top left corner




          Sure, the draw method will place origin of the string right in the point you pass. In this case solution is simple - find the size of string and make correct origin.



          let size = attributedString.size()
          let origin = CGPoint(x: textX - size.width / 2, y: textY - size.height / 2)
          attributedString.draw(at: origin)


          Result:



          enter image description here






          share|improve this answer













          I think the problem is that NSAttriutedString.draw always draws the text from the top left corner




          Sure, the draw method will place origin of the string right in the point you pass. In this case solution is simple - find the size of string and make correct origin.



          let size = attributedString.size()
          let origin = CGPoint(x: textX - size.width / 2, y: textY - size.height / 2)
          attributedString.draw(at: origin)


          Result:



          enter image description here







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 17 at 15:28









          pacification

          3,08731434




          3,08731434








          • 1




            Thank you! It worked! I never thought it was this simple! I guess I'm just bad at maths...
            – Sweeper
            Nov 17 at 15:37














          • 1




            Thank you! It worked! I never thought it was this simple! I guess I'm just bad at maths...
            – Sweeper
            Nov 17 at 15:37








          1




          1




          Thank you! It worked! I never thought it was this simple! I guess I'm just bad at maths...
          – Sweeper
          Nov 17 at 15:37




          Thank you! It worked! I never thought it was this simple! I guess I'm just bad at maths...
          – Sweeper
          Nov 17 at 15:37


















           

          draft saved


          draft discarded



















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53351327%2fhow-do-i-find-an-appropriate-position-for-my-labels-on-a-pie-chart%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Costa Masnaga

          Fotorealismo

          Sidney Franklin