UI设计 - 处理许多UIButton的最佳方式

时间:2010-10-13 20:01:43

标签: iphone performance uibutton

我在界面中处理大量UIButton时遇到了问题。我想知道是否有人有这方面的第一手经验以及他们是如何做到的?

当处理最简单的30-80个按钮时,您只需使用UIButton或做一些不同的事情,比如drawRect,响应触摸事件并获取触摸事件的坐标,这几个复杂吗?

最佳示例是日历,类似于Apples Calendar App。您是否只使用drawRect绘制大部分时间,然后单击按钮将其替换为图像或仅使用UIButtons?这不是内存占用或创建按钮,只是有时会发生奇怪的事情(之前有关它的问题)并且有性能问题为它们设置动画。

感谢您的帮助。

5 个答案:

答案 0 :(得分:2)

如果您的按钮发生了“奇怪的事情”,您需要了解 why 的底部。切换架构只是为了避免你不理解的问题(并且可能再次出现)听起来不是一个好主意。

-drawRect:通过绘制到位图支持的上下文来工作。当在-setNeedsDisplay之后调用-displayIfNeeded(或者执行隐式设置needsDisplay标志的其他操作,例如使用contentMode = UIContentModeRedraw调整视图大小)时,会发生这种情况。然后将位图支持的上下文合成到屏幕。

按钮的工作原理是将不同的组件(背景图像,前景图像,文本)放在不同的层中。文本在更改并合成到屏幕时绘制;图像只是直接合成到屏幕上。

“最好”的做事方式通常是两者的结合。例如,您可以在-drawRect中绘制文本和背景图像:因此不需要在渲染时合成不同的图层(如果您的视图是“不透明的”,则会获得额外的加速)。您可能希望通过drawRect避免全屏动画:(并且它与CoreAnimation不能很好地集成),因为绘图往往比合成更昂贵。

但首先,我会发现UIButton出了什么问题。在你真正找到慢速位之前,你可以更快地把事情做得更加令人担忧。编写代码以便于维护。 UIButton不 昂贵且-drawRect:坏(如果你使用-setNeedsDisplayInRect:作为一个小的矩形,可能会更好,但是你需要计算rect ...),但是如果你想要一个按钮,请使用UIButton。

答案 1 :(得分:1)

我宁愿使用图像(如果可能的话,单个图像或尽可能少的数字)而不是使用30-80个UIButton,而是比较触摸位置。

如果我必须创建按钮,那么显然不会为它们创建30-80个变量。我将设置并获取视图标记以确定哪个被点击。

答案 2 :(得分:1)

如果这是你动画的所有内容,那么你可以创建一堆CALayers,其内容设置为CGImage。您必须比较触摸位置以识别图层。 CALayers有一个有用的样式属性,它是一个NSDictionary,你可以存储元数据。

答案 3 :(得分:1)

我只是使用UIButton,除非碰巧遇到特定的性能问题。但是,如果它们具有类似的功能,例如键盘,我会将它们全部映射到一个IBAction,并根据发送者区分行为。

您遇到了哪些具体的性能和动画问题?

答案 4 :(得分:1)

我最近在为iPhone开发游戏时遇到了这个问题。我使用UIButtons来保存游戏牌,然后用透明图像,背景颜色和文字对它们进行风格化。

一切都适用于少量瓷砖。然而,一旦我们达到50左右,性能就会大幅下降。淘到谷歌后,我发现其他人也经历过同样的问题。看起来iPhone一下子屏幕上有很多透明按钮。不确定它是UIButton代码中的错误还是设备上图形硬件的限制,但不管怎样,它都是你作为程序员无法控制的。

我的解决方案是使用Core Graphics手动绘制板。起初它似乎令人生畏,但实际上它很容易。我刚刚在Interface Builder中的ViewController上放置了一个大的UIImageView,使它成为一个IBOutlet,所以我可以从Objective-C改变它,然后使用Core Graphics构建图像。

由于UIImageView不处理水龙头,我使用了UIViewController的touchesBegan方法,然后将触摸的x / y坐标三角测量到我游戏板上的精确图块。

董事会现在提交的时间不到十分之一秒。宾果!

如果您需要示例代码,请告诉我们。

更新:以下是我正在使用的代码的简化版本。应该足以让你获得要点。

//  CoreGraphicsTestViewController.h
//  CoreGraphicsTest

#import <UIKit/UIKit.h>

@interface CoreGraphicsTestViewController : UIViewController {
    UIImageView *testImageView;

}

@property (retain, nonatomic) IBOutlet UIImageView *testImageView;


-(void) drawTile: (CGContextRef) ctx row: (int) rowNum col: (int) colNum isPressed: (BOOL) tilePressed;

@end

...和.m文件......

//  CoreGraphicsTestViewController.m
//  CoreGraphicsTest

#import "CoreGraphicsTestViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <CoreGraphics/CoreGraphics.h>

@implementation CoreGraphicsTestViewController

@synthesize testImageView;

int iTileSize;
int iBoardSize;

- (void)viewDidLoad {
    int iRow;
    int iCol;

    iTileSize = 75;
    iBoardSize = 3;

    [testImageView setBounds: CGRectMake(0, 0, iBoardSize * iTileSize, iBoardSize * iTileSize)];


    CGRect rect = CGRectMake(0.0f, 0.0f, testImageView.bounds.size.width, testImageView.bounds.size.height);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    for (iRow = 0; iRow < iBoardSize; iRow++) {
        for (iCol = 0; iCol < iBoardSize; iCol++) {
            [self drawTile: context row: iRow col: iCol color: isPressed: NO];
        }
    }

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    [testImageView setImage: image];

    UIGraphicsEndImageContext();

    [super viewDidLoad];
}


- (void)dealloc {
    [testImageView release];
    [super dealloc];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView: testImageView];

    if ((location.x >= 0) && (location.y >= 0) && (location.x <= testImageView.bounds.size.width) && (location.y <= testImageView.bounds.size.height)) {

        UIImage *theIMG = testImageView.image;

        CGRect rect = CGRectMake(0.0f, 0.0f, testImageView.bounds.size.width, testImageView.bounds.size.height);
        UIGraphicsBeginImageContext(rect.size);
        CGContextRef context = UIGraphicsGetCurrentContext();

        [theIMG drawInRect: rect];

        iRow = location.y / iTileSize;
        iCol = location.x / iTileSize;

        [self drawTile: context row: iRow col: iCol color: isPressed: YES];


        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

        [testImageView setImage: image];

        UIGraphicsEndImageContext();
    }
}


-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UIImage *theIMG = testImageView.image;

    CGRect rect = CGRectMake(0.0f, 0.0f, testImageView.bounds.size.width, testImageView.bounds.size.height);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    [theIMG drawInRect: rect];

    [self drawTile: context row: iRow col: iCol isPressed: NO];


    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    [testImageView setImage: image];

    UIGraphicsEndImageContext();
}

-(void) drawTile: (CGContextRef) ctx row: (int) rowNum col: (int) colNum isPressed: (BOOL) tilePressed {

    CGRect rrect = CGRectMake((colNum * iTileSize), (rowNum * iTileSize), iTileSize, iTileSize); 
    CGContextClearRect(ctx, rrect);

    if (tilePressed) {
        CGContextSetFillColorWithColor(ctx, [[UIColor redColor] CGColor]);
    } else {
        CGContextSetFillColorWithColor(ctx, [[UIColor greenColor] CGColor]);
    }

UIImage *theImage = [UIImage imageNamed:@"tile.png"];
[theImage drawInRect: rrect];
}