14

I'm looking for method to add gradually fading or maybe blured border (I don't exactly know how to name this effect) to arbitrary UIView. I don't need animated effect, I need static effect, for example I my UITableView border being partially transparent. I've made the example:

enter image description here

So you can see what I'm trying to do.

Can anyone help me?

Dilip Manek
  • 9,095
  • 5
  • 44
  • 56
iKiR
  • 860
  • 2
  • 8
  • 20

5 Answers5

16

I've found a solution - I've useed CALayer's property mask:

 CALayer *viewLayer = [back layer];
 CALayer* maskCompoudLayer = [CALayer layer];

 maskLayer.bounds = viewLayer.bounds;

 [maskLayer setPosition:CGPointMake(160, CGRectGetHeight(maskCompoudLayer.frame)/2.0)];     

 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
 CGContextRef context = CGBitmapContextCreate (NULL, 320, 480, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);     

 CGFloat colors[] = {
      0.5, 0.5, 0.5, 0.0, //BLACK
      0.0, 0.0, 0.0, 1.0, //BLACK
 };     

 CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));     
 CGColorSpaceRelease(colorSpace);     

 NSUInteger gradientH = 20;
 NSUInteger gradientHPos = 0;

 CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0].CGColor);
 CGContextFillRect(context, CGRectMake(0, gradientHPos + gradientH, CGRectGetWidth(maskLayer.frame), CGRectGetHeight(maskLayer.frame)));     

 CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.0].CGColor);
 CGContextFillRect(context, CGRectMake(0, 0, 320, gradientHPos));

 CGContextDrawLinearGradient(context, gradient, CGPointMake(160, gradientHPos), CGPointMake(160, gradientHPos + gradientH), 0);     
 CGGradientRelease(gradient);     

 CGImageRef contextImage = CGBitmapContextCreateImage(context);
 CGContextRelease(context);     

 [maskLayer setContents:(id)contextImage];

 CGImageRelease (contextImage);

 viewLayer.masksToBounds = YES;
 viewLayer.mask = maskCompoudLayer;

Using this code I have UITableView with fading border

iKiR
  • 860
  • 2
  • 8
  • 20
  • Could you please elaborate on how this was actually combined with the UITableView? Thanks! – jowie Aug 25 '10 at 23:00
  • 7
    I'd also like to know how you used this specifically. Could you please post a more complete code block? What is maskLayer? What is back layer? – DoctorG Apr 03 '11 at 13:36
  • 6
    Hello there, people from 2011. I am here from 2012, and still want to know the details.. So, I guess I'll add an answer on what I came up with. – Mazyod Jul 12 '12 at 12:43
10

I'll say this first, iKiR's answer was more than enough for me. I copied the code as it is, and with little experience from my side, I was able to make it work effortlessly (on a UITableView).

  1. Create a new UIView subclass, I'll call it MaskingView
  2. import QuartzCore framework!
  3. Paste the code below in the init. (initWithCoder: and/or initWithFrame: as appropriate)
  4. Add the view that you want to apply the opacity feathering on within the MaskingView. (In Interface Builder, user Editor -> Embed In -> View. Then, choose the class of the new superview as MaskingView)
  5. Give all credit to iKiR, cause I srsly have no idea how the code works, but it simply does work!

NOTES:

  • I am developing a universal app, and the code below works as it is on both devices! (iPhone/iPad)
  • When using Embed In -> UIView, the new view will be a bit bigger than the subview. You have to make it exactly fit the subview. But, before resizing it, make sure you uncheck autoresize subviews.

The Code:

CALayer *viewLayer = [self layer];
CALayer* maskLayer = [CALayer layer];

maskLayer.bounds = viewLayer.bounds;

[maskLayer setPosition:CGPointMake(CGRectGetWidth(viewLayer.frame)/2.0, CGRectGetHeight(viewLayer.frame)/2.0)];     

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate (NULL, viewLayer.bounds.size.width, viewLayer.bounds.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);     

CGFloat colors[] = {
    0.5, 0.5, 0.5, 0.0, //BLACK
    0.0, 0.0, 0.0, 1.0, //BLACK
};     

CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));     
CGColorSpaceRelease(colorSpace);     

NSUInteger gradientH = 20;
NSUInteger gradientHPos = 0;

CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0].CGColor);
CGContextFillRect(context, CGRectMake(0, gradientHPos + gradientH, CGRectGetWidth(maskLayer.frame), CGRectGetHeight(maskLayer.frame)));     

CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.0].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, 320, gradientHPos));

CGContextDrawLinearGradient(context, gradient, CGPointMake(160, gradientHPos), CGPointMake(160, gradientHPos + gradientH), 0);     
CGGradientRelease(gradient);     

CGImageRef contextImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);     

[maskLayer setContents:(__bridge id)contextImage];

CGImageRelease (contextImage);

viewLayer.masksToBounds = YES;
viewLayer.mask = maskLayer;
Mazyod
  • 22,319
  • 10
  • 92
  • 157
3

There's a tutorial at Cocoanetics that does that exact thing.

RyeMAC3
  • 1,023
  • 1
  • 10
  • 17
  • This only works if you're using a static colored background, won't work on image backgrounds as this tutorial adds a colored gradient on top of the view you want to "fade out" – Hjalmar Jan 21 '14 at 09:49
3

You might try placing a semi-transparent PNG file over the bottom of the UITableView.

greg
  • 4,843
  • 32
  • 47
  • 3
    Yes, but in this case I'll have to have different PNGs for different backgrounds, I would like to find a universal solution – iKiR Feb 24 '10 at 19:08
1

Here's iKiR and Mazyod's answer, translated to Xamarin.iOS (Monotouch). Note that there's no need to draw rectangles before and after the gradient if you just pass the right flags to the the DrawLinearGradient method:

var vl = myView.Layer;
var l = new CALayer ();
l.Frame = new RectangleF(vl.Frame.Width/2, vl.Frame.Height/2,
            vl.Frame.Width, vl.Frame.Height) ;
var cs = CGColorSpace.CreateDeviceRGB ();
var g = new CGBitmapContext (null, 
            (int) vl.Bounds.Size.Width, (int)vl.Bounds.Size.Height, 
            8, 0, cs, CGImageAlphaInfo.PremultipliedLast);
var colors = new CGColor[] { UIColor.FromWhiteAlpha(1, 0).CGColor, 
            UIColor.FromWhiteAlpha(1, 1f).CGColor };
var grad = new CGGradient (cs, colors, new float[] { 0f, 1f });
int gradH = 20, gradHPos = 0;
g.DrawLinearGradient (grad, 
            new PointF (l.Frame.Width / 2, gradHPos), new PointF (l.Frame.Width / 2, gradHPos + gradH), 
            CGGradientDrawingOptions.DrawsBeforeStartLocation | CGGradientDrawingOptions.DrawsAfterEndLocation);
grad.Dispose ();

l.Contents = g.ToImage ();
g.Dispose ();

vl.Mask = l;
vl.MasksToBounds = true;
Diego
  • 18,035
  • 5
  • 62
  • 66