Search code examples
iosrubyrubymotion

RubyMotion and Pointers


I'm an Objective-C noob, have searched high and low not finding the answer to this yet:

In my RubyMotion project I have a UIView subclass called StatusGuage, which contains a method called drawLinearGradient as follows:

def drawLinearGradient(context, rect, startColor, endColor)
  colorspace = CGColorSpaceCreateDeviceRGB()
  locations = [0.0, 1.0]
  # colors = NSArray.arrayWithObjects(startColor, endColor, nil)
  # ptrColors = Pointer.new(:object, colors)
  colors = [startColor, endColor, nil]
  # CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef) colors, locations);
  CGGradientCreateWithColors(colorspace, colors, locations)
end

I'd like to know how to call CGGradientCreateWithColors. It clearly expects a (CFArrayRef) pointer but I cannot figure out how to pass that in. One of the iterations that I've tried is commented out.

Here is the error message:

2012-05-11 16:57:36.331 HughesNetMeter[34906:17903] 
*** Terminating app due to uncaught exception 'TypeError', 
  reason: 'status_guage.rb:43:in `drawLinearGradient:': expected 
  instance of Pointer, got `[0.0, 1.0]' (Array) (TypeError)
    from status_guage.rb:13:in `drawRect:'

Thanks for any help.


Solution

  • A couple of things. The error is not talking about the colours, it is referring to the const CGFloat locations[] argument.

    This should be a pointer which can be achieved like this (Reference on Pointer class)

    locations = Pointer.new(:float, 2)
    locations[1] = 1.0
    

    Next up your array does not need the nil termination. In Ruby this would create an array with 3 objects, which is not what you want as it will most likely cause the CGGradientCreateWithColors() function to mess up


    This looks like the example from http://www.raywenderlich.com/, so here's the rest from that

    def drawLinearGradient(context, rect, startColor, endColor)
      colorspace = CGColorSpaceCreateDeviceRGB()
      locations = Pointer.new(:float, 2)
      locations[1] = 1.0
    
      colors = [startColor, endColor]
      gradient = CGGradientCreateWithColors(colorspace, colors, locations)
    
      startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect))
      endPoint   = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect))
    
      CGContextSaveGState(context)
      CGContextAddRect(context, rect)
      CGContextClip(context)
      CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)
      CGContextRestoreGState(context)
    end
    

    Final side note

    The locations argument isn't even required in this case as CGGradientCreateWithColors() will automatically set the values to 0.0 and 1.0 for the first and last colour. Check the CGGradient Reference

    locations
    The location for each color provided in components. Each location must be a CGFloat value in the range of 0 to 1, inclusive. If 0 and 1 are not in the locations array, Quartz uses the colors provided that are closest to 0 and 1 for those locations.
    If locations is NULL, the first color in colors is assigned to location 0, the last color incolors is assigned to location 1, and intervening colors are assigned locations that are at equal intervals in between.