代码之家  ›  专栏  ›  技术社区  ›  Rudiger

用户界面设计-处理许多用户界面按钮的最佳方法

  •  2
  • Rudiger  · 技术社区  · 15 年前

    在我的界面中处理大量uibuttons时,我遇到了一些问题。我想知道是否有人对此有亲身体验,他们是如何做到的?

    在处理30-80个按钮时,最简单的方法是使用uibutton还是使用drawrect等其他方法,响应触摸事件并获取触摸事件的坐标?

    最好的例子是日历,类似于苹果日历应用程序。你会用drawrect绘制大部分的日子,然后当你点击一个按钮,用图片替换它,还是只使用uibuttons?与内存占用或创建按钮没有多大关系,只是有时会发生一些奇怪的事情(之前的问题),并且在动画时出现性能问题。

    谢谢你的帮助。

    5 回复  |  直到 15 年前
        1
  •  2
  •   tc.    15 年前

    如果你的按钮发生了“奇怪的事情”,你就需要了解 为什么? . 切换架构只是为了避免一个你不理解的问题(可能会再次出现),这听起来不是一个好主意。

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

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

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

    但首先,我会发现uibutton出了什么问题。在你真正发现慢位是什么之前,你不必担心如何使事情变得更快。编写代码以便维护。UIButton不是 那个 贵而不贵 那个 坏的(如果你使用 -setNeedsDisplayInRect: 对于一个很小的矩形,但是你需要计算矩形…),但是如果你想要一个按钮,使用uibutton。

        2
  •  1
  •   taskinoor    15 年前

    与使用30-80 ui按钮不同,我更喜欢使用图像(如果可能,使用单个图像或尽可能小的数字)并比较触摸位置。

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

        3
  •  1
  •   Ben    15 年前

    如果这些都是你要制作的动画,那么你可以创建一组将内容设置为CGIMAGE的炉管。您必须比较触摸位置以识别层。calayers有一个有用的样式属性,它是一个nsdictionary,可以在其中存储元数据。

        4
  •  1
  •   Peter DeWeese    15 年前

    我只是使用uibuttons,除非有一个特定的性能问题突然出现。但是,如果它们具有类似的功能(如键盘),我会将它们全部映射到一个i操作,并根据发送者区分行为。

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

        5
  •  1
  •   Axeva    15 年前

    我最近在为iPhone开发游戏时遇到了这个问题。我用uibuttons来保存游戏方块,然后用透明的图像、背景色和文本将它们格式化。

    对于少量的瓷砖来说,这一切都很好。然而,当我们到达50左右时,性能显著下降。在搜索了谷歌之后,我发现其他人也遇到了同样的问题。似乎iPhone在屏幕上同时使用了许多透明的按钮。不确定它是uibutton代码中的一个bug还是设备上图形硬件的一个限制,但是不管怎样,它都超出了作为程序员的控制范围。

    我的解决方案是用核心图形手工绘制电路板。一开始看起来很吓人,但实际上很容易。我刚在界面生成器中的视图控制器上放置了一个大的uiImageView,使其成为iboutlet,这样我就可以从Objective-C更改它,然后用核心图形构建图像。

    因为uiImageView不处理taps,所以我使用了uiViewController的touchesbegan方法,然后将touch的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];
    }