Kodeco Forums

Core Graphics Tutorial Part 2: Gradients and Contexts

Continue learning about Core Graphics including concepts like clipping, gradients, contexts, transforms, and more!


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/1972-core-graphics-tutorial-part-2-gradients-and-contexts
1 Like

One note here:

The options parameter for UIView.transitionFromView:toView:duration:options:completion: no longer support | operation. I did some survey for the reason for this and found out that UIViewAnimationOptions is now conform to OptionSetType protocol since Swift 2. That is to say you need to call union or intersect defined by the implementation of OptionSetType protocol.

In Flo project, you need to change the line of code calling UIView.transitionFromView to

let options = UIViewAnimationOptions.TransitionFlipFromLeft.union(.ShowHideTransitionViews)
UIView.transitionFromView( graphView,
                           toView: arcView,
                           duration: 1.0,
                           options: options,
                           completion:nil )

Thank you for pointing that out :slight_smile: - the swift 1.2 to 2.0 conversion tool suggests this:

UIView.transitionFromView(counterView, toView: graphView,
        duration: 1.0,
        options: [TransitionFlipFromRight, .ShowHideTransitionViews],
        completion: nil)

So you can just put the options in an array without using .union.

Nice, putting an array here makes more sense.

A small correction for the current Swift version:

When drawing the gradient for the first time in this tutorial, the line:

CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)

needs to be

CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, CGGradientDrawingOptions(rawValue: 0))

Thank you, nontomatic :slight_smile:

Swift 2.0 changed bitwise comparisons to OptionSetTypes. You can read more about them here:

Better code would be this:

CGContextDrawLinearGradient(context,
      gradient,
      startPoint,
      endPoint,
      []) 

where [] would be an array of CGGradientDrawingOptions with the necessary options in the array.

In this line, UIView.transitionFromView(GraphView, ... I get the error "Cannot convert value of type ‘GraphView.Type’ to expected argument type ‘UIView’, which makes sense, but I’m not sure what to do about it. Any tips for me?

Swift 2.0 changed bitwise comparisons to OptionSetTypes. You can read more about them here:
http://www.informit.com/articles/article.aspx?p=24202311

Try this:

      UIView.transitionFromView(graphView,
        toView: counterView,
        duration: 1.0,
        options: [.TransitionFlipFromLeft, .ShowHideTransitionViews],
        completion:nil)

Anywhere you see old code showing the bit mask or | operation, it’s probably changed to being an OptionSetType, which will be an array of all the options.

It looks neater than the bit mask operation (I think) :).

I may be misunderstanding, but I seem to be having a separate issue from that.

I already changed my code to the array syntax based on an earlier comment. The part that seems to be producing my error is the first parameter to transitionFromView , which is graphView. Xcode is telling me it’s expecting a UIView rather than a GraphView type.

Sorry if I am missing something with your explanation, and thanks for your help!

I just noticed that you said:

UIView.transitionFromView(GraphView,

GraphView is the class, but graphView is an instance of the class.

Try changing from big G to small g and see if that helps.

Of course. That’s an embarrassing mistake, thanks so much for the assistance!

Just wonder why you don’t use CAGradientLayer when you create a gradient layer. It’s quite simple than using CGContextDrawLinearGradient function. Here’s are my solution:

    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = bounds
    gradientLayer.colors = colors
    layer.insertSublayer(gradientLayer, atIndex: 0)

Thank you for adding this @tanvu. Yes, an alternative is CAGradientLayer, and I would encourage people to look into CALayers.

It’s always good to know about alternatives and be able to use either equally well. In this particular situation, where I am drawing into a context already, I personally would use CGContextDrawLinearGradient as I have points in the method all worked out for masking.

Hello,
I was wandering what I would have to add to the ViewContoller.swift if I would like to change the data by a button. So like show the monthly data and then the graph shows the new data.
@IBAction func MonatAnzeigen(sender: AnyObject) { graphView.graphPoints = [320,321,333,310,219,284,312,313, 321, 164, 192, 430, 130, 122,231,123,318,275,391,258,283,192,371,293,371,263,192,374,400,300,123] setupGraphDisplay() }
If I impelemt the func like this only the labels change due to the setupGraphDisplay. How do I refresh the View as well?

Thanks for the Help!
Best
Daniel

@wolfi1991

Have you tried

graphView.setNeedsDisplay()

?

@caroline

Thank you! I’ve just tried it and it works perfectly. Thank you for your help

Best
Daniel

I am Struggling with the graph and getting an error with these lines of code.

graphPath.moveToPoint(CGPoint(x:columnXPoint(0),
y:columnYPoint(graphPoints[0])))

for i in 1…<self.graphPoints.count {
let nextPoint = CGPoint(x:columnXPoint(i),
y:columnYPoint(graphPoints[i]))
graphPath.addLineToPoint(nextPoint)

I am getting the error variable used within its own initial value… Looked all around and tried to mess with it myself. Any help would be greatly appreciated. thanks

@icculus - the for loop you’ve written above doesn’t make sense.

This code:

let graphPath = UIBezierPath()
graphPath.moveToPoint(CGPoint(x:columnXPoint(0),
                              y:columnYPoint(graphPoints[0])))

for i in 1..<graphPoints.count {
  let nextPoint = CGPoint(x:columnXPoint(i),
                          y:columnYPoint(graphPoints[i]))
  graphPath.addLineToPoint(nextPoint)
}

works for me.

This is a great discussion, and it really helped point me in the right direction. Swift 3 introduced a number of additional changes to the UIView.transition syntax, and the resulting code is:

  UIView.transition(from:       counterView,
                    to:         graphView,
                    duration:   1.0,
                    options:    [.transitionFlipFromRight,
                                 .showHideTransitionViews],
                    completion: nil)
1 Like

In the GraphView class, inside the draw(_ rect:) function we use addClip() to add the rounded corners. I understand how this works, for the documentation explains it pretty clearly:

This method modifies the visible drawing area of the current graphics context.

I do have one question though: How is this operation able to work before the line:
let context = UIGraphicsGetCurrentContext()

This throws me a curve, in that we can affect the context before we get the context back. How does this work?

Unless the context is created as soon as the draw(rect:) function is called.