Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could I add a shadow to a grouped UITableView (as seen in the official twitter app)?

I have a standard grouped table view. I would like to add a shadow around it (i.e. around the edge of each tableview section) - if you are not sure what I mean, then see the official twitter app (below) for an example. It's pretty subtle, but it's definitely a shadow as opposed to a border.

Twitter app

How can I achieve this effect?

Save for using images with built in shadows as each cell's background - which won't allow animated cell resizing like I need - I haven't figured out a way.

like image 625
Jordan Smith Avatar asked Dec 24 '11 22:12

Jordan Smith


3 Answers

I have two solutions that might help. Because of the rounded corners, I think you will have to apply the shadow effect to the cells, and position them so that the shadows do not overlap on the y axis. So...

Solution 1 (the complicated solution): This is the solution that I think could work if you are animating the cornerRadius, or are unsure of the exact shape of the section of cells. Have you tried using the shadowPath like Ecarrion suggests, but applying it to your custom UITableView cell? Imagine a path for the central cells (not the top and bottom) that is slightly wider than the cell on the x axis and shorter on the y axis.

Then you have to make sure the shadow is cast at the top and bottom of the section without making each individual shadow overlap, right? So for the top and bottom cells you make the shadowPath larger on the y axis, let's say 4 points larger. Then you adjust the shadowOffset property for those cells, also by 4 points, on the y axis. If the shadowOffset of a central cell is (0,0), then the top cell is (0,-4) and the bottom cell is (0,4).

If it was me, I would put the various size and offset adjustments in a plist so I could tinker with the small details without editing the code. Just load the plist into a dictionary or custom class and then set your properties using those values. Makes the little adjustments a lot less fidgety.

EDIT: Going beyond using shadowPath, you can make a composite cell for the tableview with a transparent background layer a slightly smaller shadow layer, and yet a smaller layer for adding text and images. The shadow layer is based on the text/image layer, maybe slightly wider, taller, shorter etc. Apply a blur to that layer, and then mask it with a rectangular mask so that where one shadow layer ends the next begins. With the top and bottom layers, the mask must extend up or down to the highest/lowest point on the y axis that the shadow effect appears (you still have to mask the side that abuts the next cell). It depends on how far you're willing to go to achieve an exact effect... maybe this gives you some more ammunition.

Solution 2 (the somewhat easier solution): If you know exactly what the cornerRadius will be on the top and bottom cells, and the only thing you don't necessarily know is the length and/or width of the cells, you can use the old "stretched one-pixel graphic" trick.

First you Photoshop/Gimp an image for the corner shadow. If the width is always the same, this image can be the shadow for the whole top of the cell. The same image can be used for the other corners or the bottom of the cell by rotating it.

Now the trick. Take a 1 x 5 section of the blur or gradient effect you used for the corner shadow. If your shadow effect is 3 pixels, make that 1x3, etc. Export to PNG or preferred format and check that if you place its edge against an edge of the corner image it lines up seamlessly (the blur or gradient has the same color values without a visible seam).

All cells get a layer next to the visible part of the cell (to the left and right) with your 1x5 image as the layer contents. Could also be a stretched UIImageView, your choice. These line up the make the left and right shadows.

Place your corner shadows on the first and last cells in the section -- they can be different custom cells, or you can design your cell so that it gets its index passed to it at creation and arranges itself appropriately. Align your side shadows so that they start where the corner shadow ends, and stretch down to the bottom of the cell. If you are also changing the width of cells, you'll do the same thing between the two corner images, stretching your gradient across the x axis.

Ok, a picture says a thousand words, so since I seem to be pushing that many words here's an image to illustrate:

stretching a 1px image for a scalable border or shadow effect

like image 109
Rab Avatar answered Oct 26 '22 13:10

Rab


I needed this effect in one of my own applications the other day. It's actually very easy to achieve by just using the CALayer shadow properties in combination with a CALayer mask. I've put together a simple example project at Github:

https://github.com/schluete/GroupedTableViewWithShadows

The effect is implemented in the UITableViewCell category UITableViewCell+CellShadows.m.

The first step is to create a shadow rectangle. Enlarge the rectangle depending on the cell's position in the section (top, middle, bottom) to prevent the rounded corners from being visible in the wrong corners (lines 18-23). Then add the shadow to the background view of the cell (lines 35-41).

Now there's a nice shadow effect, but the shadow of the first cell "bleeds" into the second cell. To prevent the bleeding just add a layer mask to cut off all the unnecessary parts of the shadow (lines 25-32 and 43-46). That's it, we've got our shadow!

like image 14
Axel Schlueter Avatar answered Oct 26 '22 13:10

Axel Schlueter


Actually you could try to use a normal mode tableView (not grouped) and create 2 (or more) kind of cells with a specific identifier for each. One for the top one with top/left/right shadow, one for the last with left/right/bottom shadow, and one for the other cells with left/right shadow. In your tableView:cellForRowAtIndexPath: just check at which row you are (ex. indexPath.row == 0) and return the proper cell.

like image 2
Vincent Zgueb Avatar answered Oct 26 '22 11:10

Vincent Zgueb