自定义tableView:圆角半径、减小宽度和阴影

5
这是我想要做的事情:
如您所见,我想要:
1. 减小tableView的宽度(我希望两侧有更多的边距,而分组的tableView提供的边距不够)。 2. 圆角半径(比分组tableView默认值更大)。 3. 在表格周围添加投影,并在最后一个单元格下方添加特殊的阴影。
2个回答

4
您可以通过自己“绘制”单元格的backgroundView来实现这一点。
如果单元格的高度都相同,我建议使用一张图片作为背景。
您需要三张图片。
一张“顶部”图片,带有顶部圆角。
一张“底部”图片,带有底部圆角和所需的投影效果。
以及一张不带圆角的“中间”图片。
如果单元格内没有任何纹理或渐变,则可以使用可拉伸图像来减少其内存占用。
然后,我会子类化UITableViewCell并覆盖backgroundView以添加UIImageView。我还将提供一个访问器方法来更改单元格的类型(顶部,中部,底部)。
每个单元格都可以具有三个UIImage(topImage,bottomImage和middleImage)的占位符属性。更改单元格的类型时,可以访问这些属性(使用惰性实例化确保它们在需要时仅加载一次),然后将backgroundVIew图像设置为所需的图像。
像这样...
在UITableViewCell子类中定义一个类型枚举...
typedef enum {
    CellTypeTop,
    CellTypeMiddle,
    CellTypeBottom
} cellType;

然后为类型添加一个属性...

@property (nonatomic) cellType cellType

那么在.m文件中...
定义一些更多的内部属性...
@property UIImageView *bgImageView;
@property UIImage *topImage;
@property UIImage *middleImage;
@property UIImage *bottomImage;

然后添加imageView(仅一次)...
- (void)awakeFromNib //or in the init depends how you are initialising the cell
{
    self.bgImageView = [[UIImageView alloc] initWithFrame:blah];

    [self.backgroundView addSubView:self.bgImageView];
}

现在当类型被改变时...
- (void)setCellType:(cellType)cellType
{
    switch(cellType) {
        case CellTypeTop:
            self.bgImageView.image = self.topImage;
            break;
        case CellTypeMiddle:
            self.bgImageView.image = self.middleImage;
            break;
        case CellTypeBottom:
            self.bgImageView.image = self.bottomImage;
            break;
    }
}

最后,图像的惰性实例化...

- (UIImage *)topImage
{
    if (_topImage == nil) {
        _topImage = [UIImage imageNamed:@"topImage"];
        //alternatively...
        _topImage = [[UIImage imageNamed:@"topImage"] stretchableImageWith...
    }

    return _topImage;
}

现在对其他图片也进行相同的操作。
这将比使用CALayer替代方案更加高效(远远超过),特别是如果使用可拉伸的图像,将具有非常小的内存占用。
一些其他用户表示,这不利于性能、内存、设计等方面,但这确实是获得UserExperience最佳性能的最佳方法,优于CALayers。是的,它将使用比CALayers略多的内存,但仅有少量可出队列的单元格被创建时,它将达到极限。
以下是几个链接,解释在滚动视图中使用CALayers时可能出现的性能问题...

http://www.quora.com/iOS-Development/What-is-the-best-way-to-optimize-the-performance-of-a-non-paging-but-view-recycling-UIScrollView-involving-loading-potentially-caching-and-displaying-bundled-images

滚动视图加载3个视图控制器时性能差,使用CALayer绘制

::编辑:: 回答迈克尔的问题。

  1. In the storyboard create a UITableViewController (rename the Class in the inspector so that it matches your subclass UITableViewController - I'll call it MyTableViewController).

  2. Create a subclass of UITableViewCell (I'll call mine MyTableViewCell) in the code (i.e. the .h and .m).

  3. Add the above code to do with properties and types and imageViews to your MyTableViewCell.h file.

  4. In the storyboard select the cell in the TableViewController and rename the class to MyTableViewCell. Also set the reuse identifier on it.

  5. In the MyTableViewController code you will need a function like this...

    -(UITableViewCell*)tableView:(UITabelView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
    {
        MyTableViewCell *cell = [tableView dequeueCellWithReuseIdentifier:@"Cell"];
    
        cell.cellType = CellTypeTop; //or whichever it needs to be
        cell.textLabel.text = @"Blah";
    
        return cell;
    }
    

哦,还有一件事,在storyboard中,您将能够布置单元格的外观并链接所有标签和图像视图等... 确保为UIImageView添加IBOutlet,以便在storyboard中链接它。


这并不会提供更好的性能。图片需要更多的内存来加载,看起来可能更好,但实际上并非如此。CALayer使用较少的内存,并且在分配完成后会释放该内存。而图像将一直挂在那里,直到您释放它为止。总之,这并不能提供更好的性能。 - Popeye
抱歉,我是新手,所以需要澄清一些事情。如果我错了,请纠正我:1.在Storyboard中创建tableViewController 2.创建一个继承自UITableViewCell的新类 3.将代码添加到该类中 4.在cellForRowAtIndexPath中,您使用自定义类而不是常规的UITableViewCell。 - Michael
1
嘿@JohnDude,你能详细说明一下为什么这不是一个好的设计实践吗?根据我的经验,在滚动视图中使用阴影的第二佳方法是根本不使用阴影作为最佳方法。 - vikingosegundo
1
@JohnDude,请参考WWDC12,Session 238 - iOS应用程序性能:图形和动画,12:50。它警告不要过度使用图层阴影。 - vikingosegundo
@Fogmeister,看那边:JohnDude变成了Popeye。 - vikingosegundo
显示剩余2条评论

0

确保你已经导入了#import <QuartzCore/QuartzCore.h>,然后你就可以开始访问UITableView的层级了。

  UITableView *yourTable =  [[UITableView alloc] initWithStyle:UITableViewStyleGrouped];

  [[yourTable layer] setCornerRadius:10.0f];
  [[yourTable layer] setShadowColor:[[UIColor blackColor] CGColor]];
  [[yourTable layer] setShadowOffset:CGSizeMake([CALayer ShadowOffSetWidthWithFloat:10.0f], [CALayer ShadowOffSetWidthWithFloat:10.0f])];
  [[yourTable layer] setShadowOpacity:[CALayer ShadowOpacity:1]];
  [[yourTable layer] setMasksToBounds:NO];
  UIBezierPath *path = [UIBezierPAth bezierPathWithRect:yourTable.bounds];
  [[yourTable layer] setShadowPath:[path CGPath]];

这将为您的表视图添加阴影效果,阴影不会被遮罩到UITableView的边界上,在setCornerRadius中,您可以将表格的角设置为任何您想要的形状。您还可以通过执行以下操作来设置UITableViewframe

  [yourTable setFrame:CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)];

编辑

如另一位用户试图指出的那样,CALayer非常慢,这不是事实。

CALayer被引入以帮助处理动画性能问题。请阅读文档。直接加载图像可能看起来是个好主意,但从长远来看会占用更多内存。请参阅有关图像内存分配的问题。正如您所看到的那样,它可能看起来更快,但每个图像将占用2.25兆字节的内存,在加载每个图像这么多次后,您的应用程序将开始变慢。


1
@Fogmeister 这是访问圆角半径等的正确方式,将其仅作为图像处理是不好的设计且效果便宜。虽然CALayer可能不是最佳性能方案,但这是正确的方法。这不应该被给予差评。 - Popeye
1
我不同意,这不是“正确的方法”,而是数百种方法中的一种。您可以将UIBezierPath绘制到UIView中并遮罩等区域...从性能角度来看,它也比使用UIImage作为表格背景要慢。实际上,它非常慢,以至于我从像这样使用CALayer转而使用UIImage,在我拥有的应用程序之一中获得了非常相似的表格外观。 - Fogmeister
@Fogmeister 我经常使用这个功能,没有遇到任何问题。仅仅在上面添加一个图片并不是一个好的设计,实际上,在加载时间和效果两方面来看,使用CALayer的处理速度比加载图片更快。 - Popeye
@Popeye - 实际的性能测试经验表明你是不正确的。以这种方式使用图像在性能上远远优于CALayer的阴影和圆角。 - Fogmeister
1
即使是苹果也告诉你,这不是最好的方法:[WWDC12,Session 238 - iOS应用程序性能:图形和动画,12:50] (https://deimos.apple.com/WebObjects/Core.woa/BrowsePrivately/adc.apple.com.16351493766.016351493772.16495242607?i=1239179764) - vikingosegundo
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接