Gradients

How to create a gradient like used in the Health app? Any iOS declares for this?

I need a rounded rectangle with gradient.

For OSX I can do this without declares.
But Picture isn’t available for iOS.
I can change this to IOSBitmap but then we need RGB which isn’t available too.

Any ideas to create a gradient in iOS?

[code]function CreateGradient (w as integer, h as integer, s as color, e as color)
dim p as picture
p = newpicture(w,h,32)

dim i as integer
dim y,samt,eamt as double
for i = 0 to h
samt = 1 - (i / h)
eamt = i / h
p.graphics.forecolor = rgb(_
(s.red * samt) + (e.red * eamt),_
(s.green *samt) + (e.green * eamt),_
(s.blue * samt) + (e.blue * eamt)_
)
p.graphics.drawline -1,i,w+1,i
next

return p[/code]

[quote=175444:@Christoph De Vocht]For OSX I can do this without declares.
But Picture isn’t available for iOS.
I can change this to IOSBitmap but then we need RGB which isn’t available too.

Any ideas to create a gradient in iOS?

[code]function CreateGradient (w as integer, h as integer, s as color, e as color)
dim p as picture
p = newpicture(w,h,32)

dim i as integer
dim y,samt,eamt as double
for i = 0 to h
samt = 1 - (i / h)
eamt = i / h
p.graphics.forecolor = rgb(_
(s.red * samt) + (e.red * eamt),_
(s.green *samt) + (e.green * eamt),_
(s.blue * samt) + (e.blue * eamt)_
)
p.graphics.drawline -1,i,w+1,i
next

return p[/code][/quote]

Is it not RGB : http://developer.xojo.com/color$RGB

Thanks. That solves the RGB issue. But the code doesn’t seems to work.
Mask isn’t possible it seems.

[quote=175482:@Christoph De Vocht]Thanks. That solves the RGB issue. But the code doesn’t seems to work.
Mask isn’t possible it seems.[/quote]

Can you post the code you are trying to use for iOS ?

Apart from p.graphics.Forecolor which should be p.graphics.linecolor there should not be much differences. And you should not need a mask, since you can use RGBA to set the Alpha channel. In the Dim, drop the 32 to keep the picture transparent.

For those Declare gurus… perhaps this will help…

clr1=UIColor(rgb: 0x59A3E0)
clr2=UIColor(rgb: 0x427AA8)

 let startPoint:CGPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect))
let endPoint  :CGPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect))
 let colors: [CGFloat] = [clr1.red(),clr1.green(),clr1.blue(),1.0, clr2.red(),clr2.green(),clr2.blue(),1.0]
let baseSpace:CGColorSpaceRef = CGColorSpaceCreateDeviceRGB()
let gradient :CGGradientRef  = CGGradientCreateWithColorComponents(baseSpace, colors, [1.0,0.0], 2)
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)

Here is exactly what you want:

  Dim startColor As Color = &cff0000
  Dim endColor As Color = &c0000ff
  
  Dim p as New Picture(canvas1.Width, canvas1.Height)
  
  Dim samt, eamt As Double
  
  For i As Integer = 0 To p.Height
    samt = 1 - (i / p.Height)
    eamt = i / p.Height
    p.Graphics.ForeColor = RGB((startColor.Red * samt) + (endColor.Red * eamt), _
    (startColor.Green *samt) + (endColor.Green * eamt), _
    (startColor.Blue * samt) + (endColor.Blue * eamt))
    p.Graphics.FillRoundRect(0,i,canvas1.width,canvas1.height,30,30)
  Next
  
  g.DrawPicture(p, 0, 0)

Put that into a canvas object under a paint event

Thats about same code from my original post. This does not work in iOS because it does not handle Picture.

This partially works:

[code]Dim startColor As Color = &cff0000
Dim endColor As Color = &c0000ff

Dim p as New iOSBitmap(canvas1.Width, canvas1.Height,2)

Dim samt, eamt As Double

For i As Integer = 0 To p.Height
samt = 1 - (i / p.Height)
eamt = i / p.Height
p.Graphics.FillColor = color.RGB((startColor.Red * samt) + (endColor.Red * eamt), _
(startColor.Green *samt) + (endColor.Green * eamt), _
(startColor.Blue * samt) + (endColor.Blue * eamt))
p.Graphics.FillRoundRect(0,i,canvas1.width,canvas1.height,20,20)
Next

g.DrawImage(p, 0, 0)[/code]

This makes it half transparent :

[code] Dim startColor As Color = &cff0000
Dim endColor As Color = &c0000ff

Dim p as New iOSBitmap(canvas1.Width, canvas1.Height,2,false)

Dim samt, eamt As Double

For i As Integer = 0 To p.Height
samt = 1 - (i / p.Height)
eamt = i / p.Height
p.Graphics.FillColor = color.RGBA((startColor.Red * samt) + (endColor.Red * eamt), _
(startColor.Green *samt) + (endColor.Green * eamt), _
(startColor.Blue * samt) + (endColor.Blue * eamt),&hF9)
p.Graphics.FillRoundRect(0,i,canvas1.width,canvas1.height,20,20)
Next

g.DrawImage(p, 0, 0)[/code]

Thanks, Jean-paul, very nice example.

What is missing now is a ColorPicker, like SelectColor for desktop.

Is there a way to draw that gradient with rounded corners, as per the OP?

OK, Jean-paul,
Will have a look on it.

There’s even an easier approach to it: CALayer has a subclass CAGradientLayer. You can pass as many colors as you like and optionally define their locations. Here’s two simple 2.color semi-transparent Gradient layers stacked:

But, as nice as it looks, I have problems with autoresizing. I find in Apple’s docs hints to a CALayer AutoresizingMask property of type UInt32 but that crashes with a “selector unknown” exception. Has anyone managed sublayer autoresizing?

sorry, I was misled myself. AutoResizingMask is a property of the UIView, but I add a CAGradientLayer, a sublayer, not a subview, and they don’t have these masks. But there’s a “ResizesSubLayers” property which I thought would be able to scale the sublayers when the view’s bounds change, but nothing happens. I’ll probably have to either catch the redraw notification or override the layer’s draw method, but I stil have hope there could be an easier way.
The declare code won’t probably help so much because I use a lot of separate funtions, but in case you have an idea:

dim myarray() as color = array (&c394D7E08, &cDA770008,&cDED62D00 ) // The Gradient colors dim newlayer as new iOSLibCAGradientLayer (myarray) newlayer.Frame = ImageView1.iOSLibView.Bounds //iOSLibView is a property of the UIView class newlayer.ContentsScale = ImageView1.iOSLibView.Layer.ContentsScale // and layer a property of CALayer newlayer.RedrawOnResize = true // this is needsDisplayOnBoundsChange. newlayer.MasksToBounds = true ImageView1.CoreAnimationLayer.RedrawOnResize = true // probably unnecessary, just experimenting. //CoreAnimationlayer is a shortcut for iOSLibView.layer imageView1.iOSLibView.Layer.AddSubLayer newlayer

As shown above, everything works nicely, but only until the device is rotated.

EDIT: But if want to try yourself you find it here – click on “menu”, “CALayer properties” and there on the Button “BG”:
https://github.com/UBogun/Xojo-iosLib

EDIT II: Found it. You need to override the UIView’s layoutSubivews method with something like

Private Shared Sub impl_layoutSubviews(id as ptr, sel as ptr) dim Ego as new iOSLibView (id) if not ego.IsNIL then dim sublayers as iOSLibArray = ego.Layer.SubLayers if not sublayers.IsNIL then for q as uinteger = 0 to sublayers.Count -1 dim sublayer as new iOSLibCALayer (sublayers.PtrAtIndex(q)) sublayer.Frame = ego.Bounds next end if end if End Sub

and then use this custom control as a replacement for the control.

CAGradientLayers are ready: https://dl.dropboxusercontent.com/u/21200221/Xojo/CAGradientLayer.mov
(see also updated image above) – all on Github, see last post.

A late follow-up to this post: I just updated iOSLib (and hope everything’s fine with the repository). Biggest change is the addition of AppleCGContext and its convenience module iOSGraphicsExtension. With it, you can draw gradients (and a lot of other things) GPU-powered in the paint event of a canvas:

This is the way that I draw gradients with rounded corners on the Desktop version of Xojo.
Using no declares and all pure Xojo code (probably not the most efficient way but it works).
I draw a mask picture with the rounded corners and apply that mask to a picture with the gradient drawn into it.
I then draw that picture into the canvas.

[code]Dim topColour, bottomColour As Color, ratio, endratio as Double, CornerRadius As Integer

CornerRadius = 5
topColour = &cBFBEC400
bottomColour = &cF5F4FC00

//Make a mask with the rounded corners
Dim maskP As Picture = new Picture(g.Width, g.Height)
maskP.Graphics.ForeColor = &cFFFFFF
maskP.Graphics.FillRect(0,0,maskP.Width,maskP.Height)
maskP.Graphics.ForeColor = &c000000
maskP.Graphics.FillRoundRect(0,0,maskP.Width,maskP.Height, CornerRadius,CornerRadius)

//Make a picture to draw the gradient into
Dim p As Picture = new Picture(g.Width, g.Height,32)
Dim pg As Graphics = p.Graphics

//Draw the gradient
for y as integer = p.Height DownTo 0
ratio = (y/pg.Height)
endratio = ((pg.Height-y)/pg.Height)

pg.ForeColor = RGB(_
topColour.Red * endratio + bottomColour.Red * ratio,_
topColour.Green * endratio + bottomColour.Green * ratio,_
topColour.Blue * endratio + bottomColour.Blue * ratio)

pg.DrawLine(0,y,p.Width,y)
next

//Apply the rounded corner mask
p.ApplyMask(maskP)
//Draw the picture into the canvas
g.DrawPicture(p,0,0)
[/code]

I am sure someone could cobble together the proper declares to do this with CAGradientLayer

actually someone already did… 3 years ago and about 4 topics up from here…