UI design is the best way to deal with many UIButtons

I'm having problems working with a lot of UIButtons in my interface. I was wondering if anyone has first-hand experience with this and how did they do it?

When working with 30-80 buttons, the most simple, a few difficulties, do you just use UIButton or do something else, for example drawRect, respond to touch events and get the coordinates of the touch event?

A better example is a calendar similar to the App App Calendar. Would you just draw most of the days using drawRect, and then when you click the button, replace it with an image, or just use UIButtons? This is not so much the amount of memory or the creation of buttons, but sometimes strange things happen to them (the previous question) and performance problems that enliven them.

Thanks for any help.

+2
source share
5 answers

If “strange things” happen to your buttons, you need to understand why. Switching architectures simply to avoid a problem that you do not understand (and may arise again) does not seem to be a good idea.

-drawRect: works by drawing in context with bitmap support. This happens when -displayIfNeeded is called after -setNeedsDisplay (or does something else that implicitly sets the requireDisplay flag, for example, resizing a view using contentMode = UIContentModeRedraw). The bitmap-enabled context is then compiled on the screen.

Buttons work by placing various components (background image, foreground image, text) in different layers. Text is drawn when it changes and composes on the screen; Images are simply compiled directly on the screen.

The “best” way to do things is usually a combination of the two. For example, you can draw text and background image in -drawRect: therefore, different layers did not need to be compiled during rendering (you get additional acceleration if your view is "opaque"). You probably want to avoid full-screen animations via drawRect: (and it won't integrate so well with CoreAnimation), since a drawing tends to be more expensive than layout.

But first, I find out what happens to UIButton. There is little point in worrying about how you could make things faster until you really know what slow bits are. Write a code to make it easy to maintain. UIButton is not so expensive and -drawRect: not so bad (presumably, it is even better if you use -setNeedsDisplayInRect: for a small rectangle, but then you need to calculate rect ...), but if you want a button, use UIButton.

+2
source

Instead of using 30-80 UIButtons, I prefer to use images (if possible, one image or as few as possible) and compare the location of the touch.

And if I have to create buttons, then obviously I won’t create 30-80 variables for them. I installed and get a presentation tag to determine which one was used.

+1
source

If this is all you are animating, then you can create a bunch of CALayers with their contents set to CGImage. You must compare the location of the touch to identify the layer. CALayers have a useful style property, which is an NSDictionary in which you can store metadata.

+1
source

I just use UIButtons if there is no particular performance problem that arises. However, if they have similar functionality, such as a keyboard, I map them all to one IBAction and distinguish between sender-based behavior.

What specific performance and animation problems do you use?

+1
source

I recently ran into this problem while developing a game for the iPhone. I used UIButtons to store game plates, then styled them with transparent images, background colors, and text.

Everything worked well for a small number of tiles. However, when we got to 50, productivity dropped significantly. After cleaning Google, I found that others were experiencing the same problem. It seems that the iPhone is struggling with many transparent buttons on the screen right away. I'm not sure if this is a mistake in the UIButton code or just a limitation of the graphic equipment on the device, but in any case it is not under your control as a programmer.

My solution was to draw the board manually using Core Graphics. It seemed complicated at first, but actually it was pretty easy. I just put one big UIImageView on my ViewController in Interface Builder, made it an IBOutlet so that I could change it with Objective-C, and then built the image using Core Graphics.

Since UIImageView does not handle cranes, I used the touchhesBegan method of my UIViewController and then triangulated the x / y coordinates of the touch to the exact tile on my game board.

Now the board is less than one tenth of a second. Bingo!

If you need some sample code, just let me know.

UPDATE: Here is a simplified version of the code I'm using. It should be enough for you to get the gist.

 // 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 

... and the .m file ...

 // 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]; } 
+1
source

All Articles