I'm currently working on an iPhone app that's doing some strange things with a UIScrollView inside a UITableView. This is my first foray into iPhone dev, so I'm sure it's something silly I'm missing.
In each UITableViewCell I am putting in a basic UITableViewCell. In that UITableViewCell is a Label and a UIScrollView.
The label and scrollview is setup and working properly, but when it first displays it is offset about 30 pixels down on the y axis than it should be, or is positioned by the XIB/NIB. I am not moving it around manually. The label shows up in the right spot. at 0,0. The UIScrollView should be showing up at 0,22 but is showing up closer to 0,40.
When I swipe to scroll the containing UITableView, then all the UIScrollViews will show up in the right spot assuming that when the UITableView scrolled that UITableViewCell went offscreen.
Here is the code for the UITableView.cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"GalleryRowCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
[cell.layer setMasksToBounds:TRUE];
[cell.layer setCornerRadius:10.0];
Contagion *c = (Contagion *)[self.dataSet objectAtIndex:indexPath.row];
GalleryRowViewController *subView = [[GalleryRowViewController alloc] initWithContagion:c];
[cell.contentView addSubview:subView.view];
subView.contagionName.text = c.name;
subView.contagion = c;
return cell;
}
Here is the code for my GalleryRowViewController.viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
self.imageScroll.delegate = self;
[self.imageScroll setBackgroundColor:[UIColor blackColor]];
[self.imageScroll setCanCancelContentTouches:NO];
self.imageScroll.indicatorStyle = UIScrollViewIndicatorStyleWhite;
self.imageScroll.clipsToBounds = NO;
self.imageScroll.scrollEnabled = YES;
self.imageScroll.pagingEnabled = NO;
NSInteger x = 0;
CGFloat xPos = 0;
for (x=0;x<=10;x++) {
UIImage *image = [UIImage imageNamed:#"57-icon.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[imageView setBackgroundColor:[UIColor yellowColor]];
CGRect rect = imageView.frame;
rect.size.height = 70;
rect.size.width = image.size.width;
rect.origin.x = xPos;
rect.origin.y = 5;
imageView.frame = rect;
[self.imageScroll addSubview:imageView];
xPos += imageView.frame.size.width+5;
}
[self.imageScroll setContentSize:CGSizeMake(xPos, [self.imageScroll bounds].size.height)];
}
--- EDIT FOR IMAGES ---
After App Loads: http://img809.imageshack.us/img809/4576/screenshot20110927at427.png
After Scrolling the rows offscreen and back: http://img690.imageshack.us/img690/9461/screenshot20110927at428.png
Well, as my previous response was at too low a level, let me take another shot at it.
First, I just noticed the core problem that you're using a viewcontroller for each cell. To quote Apple, " "A single view controller typically manages the views associated with a single screen’s worth of content." That would also get rid of your XIB (just manually configuring your scrollview), which I bet will get rid of your problem.
To proceed, your main choice is whether to create a ContagionTableViewCell class or not as suggested by Scott.
If so, following the Elements example, create a subclass of UITableViewCell ContagionTableViewCell with properties of a scrollView, a labelview and a contagion. Like they use a custom setter for the element, use one for the contagion, so that whenever it is assigned, it also updates the cells label (and associated pictures).
Move your imageScroll code from GalleryRowViewController.viewDidLoad into the ContagionTableViewCell init code. Put the image code into a new routine, which will be called from the contagion setter.
If NOT, then move the GalleryRowView Controller code into your UITableView. I suggest you take a look at cellForRowAtIndexPath in Apple's tableViewSuite, the fourth example on subviews. In particular, it shows this pattern of separating the creation of a cell (when you need a brand new one) vs configuring the cell (when reusing it). As you have 10 imageViews inside your scrollView, you'll have to decide whether to delete all those (and/or the scrollview), or just reach inside and update their images when a cell is reused.
Can you post a screenshot. Its a bit hard to visualize what you are describing. I'm not sure how you are computing y origin to be 22.
As a side note I believe its cleaner to do this by creatint your own TableViewCell subclass and use that instead of the default UITableViewCell. There is an example called Elements which shows how to do this properly: http://developer.apple.com/library/ios/#samplecode/TheElements/Introduction/Intro.html
Well, I don't know it's the cause of your problem, but you've definitely got an issue. Note that every time you are asked for a cell, you're adding the galleryRow subview. When a cell goes off-screen, it's put on the reusableCell queue. Then you're asked for another cell; you get it from the queue, it still has the old galleryRow subview, and now you add another one; so that's not good. You should either reuse or delete the old one.
Finally, why are you using UITableViewCellStyleSubtitle, and then not using any of the default fields in that UITableView?
Related
I have the following view hierarchy in a UIView page:
2 UIButtons
1 ImageView
1 UILabel
1 UITableView - grouped style - contains only one cell (of type custom cell, created completely with code without any xib), the cell contains this view hierarchy:
ImageView
2 OHAttributedLabels [here's its gitHub page]
1 UILabel rotated vertically using CGAffineTransform method
I push the view controller of this view on a navigation controller using: pushViewController method, however, everything works fine except for the case of opening the application for the first time, when this view controller is pushed the view instantly flickers once (with some black strips on screen edges), when I navigate back to other views in the application, and then reopen this view, no flickering happens.
what is the cause of this flickering ?
thank you in advance.
p.s. the OHAttributedLabel is tested on other views in the application without any problems.
EDIT:
the following code is from view1.m (the view im talking about in the question above):
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIImage *backgroundImage = [UIImage imageNamed: #"bg1.png"];
UIImageView *backGroundImageView = [[[UIImageView alloc] initWithImage: backgroundImage] autorelease];
backGroundImageView.frame = CGRectMake(0,0,320,411);
[self.view addSubview: backGroundImageView];
[self.view sendSubviewToBack: backGroundImageView];
self.gsTableView.layer.cornerRadius = 10; // to make a the corner of the table view rounded
self.gsParagraphs = [NSArray arrayWithObjects:
NSLocalizedString(#"this is a test string this is a test string this is a test string this is a test string this is a test string this is a test string this is a test string this is a test string this is a test string" , #"test string1"),
NSLocalizedString(#"this is another test string this is another test string this is another test string this is another test string this is another test string this is another test string" , #"test string2"),
nil];
}
and the following code is for cellForRowAtIndexPath method in view1.m file:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *MyIdentifier = [NSString stringWithFormat:#"MyIdentifier %i", indexPath.row];
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}
cell.imageView.image = [UIImage imageNamed: #"gs_1.jpg"];
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = 5.0;
// cell.textLabel.text = [self.gsParagraphs objectAtIndex:0]; //it will become blurry
//note: the attributed label is used to easily apply justified alignment and many other options
NSMutableAttributedString* cellBody1 = [NSMutableAttributedString attributedStringWithString:[self.gsParagraphs objectAtIndex:0]];
[cellBody1 setTextAlignment:UITextAlignmentJustify lineBreakMode: UILineBreakModeWordWrap];
cell.secondTextLabel.attributedText = cellBody1;
NSMutableAttributedString* cellBody2 = [NSMutableAttributedString attributedStringWithString:[self.gsParagraphs objectAtIndex:1]];
[cellBody2 setTextAlignment:UITextAlignmentJustify lineBreakMode: UILineBreakModeWordWrap];
cell.thirdTextLabel.attributedText = cellBody2;
// cell.cellLabelTextYOffset = 10; this is necessary only for the original text label
cell.cellTitle.text = NSLocalizedString(#"Test Title", #"Test Title");
cell.cellTitleLabelWidth = 170;
return cell;
}
there is no code for viewDidAppear method
Its a little unclear why you suspect your table view controller. Is the flicker inside table's bounds?
Some suspects:
Your XIB may define one view with a black background that is covered by another view with your expected color.
a resize event can happen if the view is the wrong size by a few pixels.
a rotate event could happen when the objects are first instantiated. I have been told of a case where the view was getting a rotate from and to the same orientation which left black bars. (Not sure how that happened, but it was logged from within the delegate methods.)
If you create all the delegate methods for both the view controller and table delegates, which ones are called in what order?
I don't know if it still works, but I have used a single white button in place of a one-cell grouped table. If you never plan to add another cell, there is little need to use an entire table.
I am new to iPhone development and I am currently working on a simple RSS reader app. The problem I am having is that I need to reposition the textLabel inside the UITableViewCells. I have tried setFrame or setCenter but it doesn't do anything. Does anyone know what I need to do inside the tableView:cellForRowAtIndexPath: method to reposition the textLabel at the top of the cell (x = 0, y = 0)?
Thank you
PS: The UITableViewCell is referenced by a variable called cell. I have tried [cell setFrame:CGRectMake(0, 0, 320, 20)] with no success.
You can create a subclass for UITableViewCell and customize de textLabel frame. See that answer: Labels aligning in UITableViewCell. It's works perfectly to me.
It's my subclass
#import "UITableViewCellFixed.h"
#implementation UITableViewCellFixed
- (void) layoutSubviews {
[super layoutSubviews];
self.textLabel.frame = CGRectMake(0, 0, 320, 20);
}
#end
It's my UITableViewControllerClass:
UITableViewCellFixed *cell = (UITableViewCellFixed *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCellFixed alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
You may try indentationLevel, separatorInset and other content indentation properties of UITableViewCell object.
Seems I solved my own problem. Here's some code, in case someone runs into the same problem:
UILabel *ttitle = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 20)] autorelease];
ttitle.font = [UIFont boldSystemFontOfSize:13];
ttitle.textColor = [UIColor blackColor];
ttitle.textAlignment = UITextAlignmentLeft;
[ttitle setText:[[stories objectAtIndex: storyIndex] objectForKey: #"title"]];
[cell.contentView addSubview:ttitle];
The idea is to create your own label object, because the textLabel is automatically positioned and can't be moved around.
Cheers.
The reason the original poster's code doesn't work is that it appears that the frame of the textLabel is set after the UITableViewCell has been returned from your delegate method.
I noticed that I can successfully alter many properties of the textLabel, such as the text alignment, color, font, etc, but altering the frame has no effect and when I print the frame to the debugger later (like on select), the frame isn't what I set. Therefore, I conclude that the UIKit framework is altering the frame of the textLabel after it is returned from the delegate method. No doubt this is likely done because Apple engineers wanted to make sure that your text was drawn to the screen, so they measure it and alter the frame so that it will fit. They probably figured that people such as ourselves who wanted to alter the position of the text would be able to do so by subclassing, or simply adding another UILabel (or whatever) as a subview. A novice developer might have a very hard time if his or her text didn't show up in the label or was truncated because they didn't adjust the frame.
In my case, I wanted the text to be center horizontally, to be a specific color/font/size, and to be slightly higher vertically in the cell. Being too lazy to subclass this, I first tried altering the frame. When that didn't work, I tried googling the answer (found this post).
My final solution was to set the numberOfLines property to 0 and add some trailing carriage returns to my text. Now THAT is lazy.
In Swift 3 it would be
override func layoutSubviews() {
super.layoutSubviews()
self.textLabel?.frame.origin.x = 50
}
Before describing the problem, let me first point out that this is a distinct issue from this question.
The Problem
This screenshot was taken with a break set at tableView:didSelectRowAtIndexPath:, and as you can see in the simulator (far right of the image), there's a single-pixel blue line at the bottom of the selected cell. This is not the design asked for by the client, nor is it how this app used to behave: there should be no separator, even on selection.
How I Got Here
I'd initially designed this table view using custom UITableViewCell classes with corresponding nib (.xib) files and had no trouble with selections: the separator was hidden as desired. Predictably, scrolling was sluggish due to all the overhead from the view hierarchy, so I reworked the custom cells to use Loren Brichter's fast scrolling solution. Now scrolling is much faster, but I can't get rid of the separator for the life of me.
What I've tried
At the time of the screenshot above...
the table view has "Separator [None]" in IB.
the UIViewController that contains the table view has this line in viewDid Load: self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
As you can see in the screenshot, I inserted some unused lines to prove that separatorStyle is set as desired. Other testing confirms that tableView and self.tableView are equivalent pointers at that same breakpoint.
I've also tried setting tableView.separatorColor to black and to clear, all with the same result: the cells look right until a selection is made.
Manjunath: Here's the code I'm using to draw alternate backgrounds depending on whether the cell's been touched or not. You can see the difference—which is less subtle when animated—in the screenshot.
if(self.highlighted) {
textColor = [UIColor blackColor];
UIImage *bg = [UIImage imageNamed:#"image-cell-background_highlighted.png"];
[bg drawAtPoint:CGPointMake(0.0, 1.0)];
}
else {
UIImage *bg = [UIImage imageNamed:#"image-cell-background.png"];
[bg drawAtPoint:CGPointMake(0.0, 0.0)];
}
This gets called in UIImageCell.m in drawContentView:, a method inherited from Mr. Brichter's ABTableViewCell super class.
Chris,
Delving into ABTableViewCell, I see:
- (void)setFrame:(CGRect)f
{
[super setFrame:f];
CGRect b = [self bounds];
b.size.height -= 1; // leave room for the seperator line
[contentView setFrame:b];
}
Since the height of the cell is one pixel shorter than the actual cell, when the cell gets selected, that one-pixel line will bleed through in the color of the selection color. It may look like it's the separator, but it is actually the selection color.
To test, try to change that line above to be two pixels or more shorter to see what happens.
Update:
By making this change to the FastScrollingExample project's -rootViewController:
- (void)viewDidLoad
{
self.title = #"Fast Scrolling Example";
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[super viewDidLoad];
}
and commenting out:
// if(self.selected)
// {
// backgroundColor = [UIColor clearColor];
// textColor = [UIColor whiteColor];
// }
//
in -drawContentView to mimic what would happen if you didn't have the selection color showing through, then I get a screen shot like this:
alt text http://files.me.com/mahboud/7k656q
Look familiar?
How would you get around this? If you don't need to select cells, then disable cell selection. Otherwise, if you are selecting cells, then you should make the rect larger so the default selection color doesn't show through when you paint with your own selection color in -drawConentRect.
Try this:
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = NSLocalizedString(#"Cell",#"");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
Check out the last lines before [return cell]..After the images are being loaded the scrolling speed is decreasing..it seems the scroll gets stuck
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
NSString *itemDescription=[[stories objectAtIndex: storyIndex]
objectForKey:#"title"];
CGRect aframe = CGRectMake(80, 30, 250, 40);
textLabel = [[UILabel alloc] initWithFrame:aframe];
textLabel.font = [UIFont boldSystemFontOfSize:14];
textLabel.numberOfLines=0;
textLabel.textColor = [UIColor darkTextColor];
textLabel.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:textLabel];
textLabel.text=itemDescription;
CGRect frame = CGRectMake(0, 0, 70,80);
UIImageView *TopImageView = [[UIImageView alloc] init];
[cell.contentView addSubview:TopImageView];
TopImageView.frame=frame;
m_strImage = [m_imglinkArray objectAtIndex:storyIndex];
TopImage = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:m_strImage]]];
TopImageView.image=TopImage;
return cell;
}
Could you guys help me to increase the speed of the scroll?
Regards
Arun
I believe you may have two problems here. First is the issue of not multi-threading the image loads and second is how you're using UITableViewCell.
iPhone calls cellForRowAtIndexPath whenever a cell appears on the screen - if you scroll down, everything above is unloaded and reloaded when you scroll back up again. As already noted this can be solved with multithreading patterns - either the link posted to markj.net, and/or by using NSThread. The advantage of NSThread is you have more flexibility to handle problems or load more than just image data. (I would use both)
In order to do this efficiently you need to have a custom UITableViewCell. This is good to have regardless because you can make the table cell responsible for it's content rather than your view controller, and you can efficiently manipulate cell formatting and content. Here is a link to a great post about UITableCellView: http://blog.atrexis.com/index.cfm/2009/1/6/iPhone--Customize-the-UITableCellView
When you implement UITableViewCell, do your best to put all addSubView and formatting calls in the "if(cell==nil)" block, only if you can't put them in your custom initWithFrame. You'll find the iPhone is stacking every subview you add -- it's not clearing the old view. You can see this at work by setting all the background colors to "UIColor clearColor" and then change text between hits -- you'll see a stack of text on top of each other. This isn't normally visible because solid-filled backgrounds draw over all the "old" subviews.
Before you combine these two methods, considering implementing a "model". You've got a view and a controller but all of your data should be in a model. All of the image URLs, content, etc. should be in something like an NSMutableArray, a custom object of your own, or maybe via SQLite. Once you have a model, you can implement your caching of the images. With caching, all of the images will be retained between loads of your application which will save batteries, bandwidth, and time.
I would:
1. put all your data in a model of some kind (NSMutableArray, SQLLite, something)
2. Implement your own custom UITableViewCell
3. Make sure you have 0 addSubView, init, or alloc calls inside the active block of cellForRowAtIndexPath
4. Use delegation example or NSThread to load all images in the background
5. Add local image caching
There are some good examples out in about and on the Apple forums on caching images.
nessence has a lot of good information. A few more thoughts:
You're leaking like crazy here. Every cell you create, you leak 2 UILabels, a UIImageView and a UIImage.
As noted before, not only are you leaking these, they're accumulating in your view because you're sticking one on top of the other with addSubview:.
Reaching out to the network during a cell draw is incredibly slow and is blocking your UI. If these URLs are local, then you can use UIImage's +imageWithContentsOfFile, if not, you need to load this in the background.
I don't think you need a thread here. NSURLConnection is an excellent way to load data in the background without incurring the overhead of threading.
nessence is completely correct that you need a model class for Story.
Your basic approach to reusable cell configuration is incorrect. You don't fetch a reusable cell and then add subviews to it. All your subviews should be added in the if() block to create the cell. Then, in each pass, you just change the values of things. I've rewritten some of your code below to demonstrate. This is still not correct code, because it is reaching out to the network during a cell draw, and this may be too many elements to be in a subview-cell (rather than a custom cell), but it's closer to the right idea. I don't even know if this compiles; I just typed it here.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
// Here we do all our cell creation
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
// Make the label
CGRect aframe = CGRectMake(80, 30, 250, 40);
UILabel *textLabel = [[[UILabel alloc] initWithFrame:aframe] autorelease]; // Note the -autorelease so we don't leak
textLabel.font = [UIFont boldSystemFontOfSize:14];
textLabel.numberOfLines=0;
textLabel.textColor = [UIColor darkTextColor];
textLabel.backgroundColor = [UIColor whiteColor];
textLabel.tag = DescriptionTag; // A tag so we can find it later (you'll need a constant for this)
[cell.contentView addSubview:textLabel];
// And the second label
aframe = CGRectMake(80, 30, 250, 40);
textLabel = [[[UILabel alloc] initWithFrame:aframe] autorelease];
textLabel.font = [UIFont boldSystemFontOfSize:14];
textLabel.numberOfLines=0;
textLabel.textColor = [UIColor darkTextColor];
textLabel.backgroundColor = [UIColor whiteColor];
textLabel.tag = TitleTag;
[cell.contentView addSubview:textLabel];
// The image view
CGRect frame = CGRectMake(0, 0, 70,80);
UIImageView *topImageView = [[[UIImageView alloc] init] autorelease];
topImageView.frame = frame;
topImageView.tag = TopImageTag;
[cell.contentView addSubview:topImageView];
}
// all the above was cell creation; we do that as seldom as possible.
// Now we do cell configuration. We want this to be fast.
UILabel *descriptionLabel = (UILabel*)[cell.contentView viewWithTag:DescriptionTag];
descriptionLabel.text = itemDescription;
UILabel *titleLabel = (UILabel*)[cell.contentView viewWithTag:TitleTag];
titleLabel.text =[[stories objectAtIndex:indexPath.row] objectForKey:#"title"];
NSString *imageURLString = [m_imglinkArray objectAtIndex:storyIndex]; // You should have a model class called Story, not two arrays.
UIImage *image = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURLString]]] autorelease]; // This is still way too slow if it's a remote URL
UIImageView *imageView = (UIImageView*)[cell.contentView viewWithTag:TopImageTag];
imageView.image = image;
return cell;
}
I recommend you spend some quality time studying TableViewSuite, Practical Memory Management, and Coding Guidelines for Cocoa. Some time studying the basics of Cocoa would be useful as well, because the coding style here indicates that you may not have a solid foundation. Though it's a Mac book, I still recommend Cocoa Programming for Mac OS X. If you're interested in using this to learn iPhone, I've put together a syllabus that might help. I haven't reviewed it yet, but Stanford's online CS193P course looks promising.
this has been asked several times here, what you need to do is to load asynchronously the images for each cell in order to prevent the scroll to slow down.
This is called "Lazy image loading" and I'm referencing the apple tutorial here :
Lazy load images in UITableView
It seems that you are loading from a url every time the table view asks to get the cell for a row. This is happening in the main application thread (the User interface thread) and it blocks the user interface, making it not responsive.
Especially if this url is a resource that you load from the internet then this pause is big.
I would recommend that you should load the images in a background thread and display a placeholder image until they are fetched from the network.
You can try the solution provided in the following url...
http://www.markj.net/iphone-asynchronous-table-image/
I have a UITableView with reorderable rows and I'm using the standard UITableViewCell.text property to display text. When I tap Edit, move a row, tap Done, then tap the row, the built-in UILabel turns completely white (text and background) and opaque, and the blue shade to the cell doesn't show behind it. What gives? Is there something I should be doing that I'm not? I have a hacky fix, but I want the real McCoy.
Here is how to reproduce it:
Starting with the standard "Navigation-Based Application" template in the iPhone OS 2.2.1 SDK:
Open RootViewController.m
Uncomment viewDidLoad, and enable the Edit button:
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
Specify that the table has a few cells:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 4;
}
In tableView:cellForRowAtIndexPath:, add a line to set the text property of a cell, and therefore to use the built-in UILabel subview:
// Set up the cell...
cell.text = #"Test";
To enable reordering, uncomment tableView:moveRowAtIndexPath:toIndexPath:. The default implementation is blank, which is fine in this case since the template doesn't include a data model.
Configure the project for the Simulator, OS 2.2.1, Build and Go. When the app comes up, tap Edit, then slide any row to a new position, tap Done, and then tap each row one at a time. Usually a tap will select a row, turn it blue, and turn its text white. But a tap on the row that you just moved does that and leaves the UILabel's background color as white. The result is a confusing white open space with blue strips on the edges. Oddly enough, after the first bogus tap, another tap appears to correct the problem.
So far I have found a hack that fixes it, but I'm not happy with it. It works by ensuring that the built-in UILabel is non-opaque and that it has no background color, immediately upon selection.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// hacky bugfix: when a row is reordered and then selected, the UILabel displays all crappy
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
for (UIView *view in cell.contentView.subviews) {
if ([[view class] isSubclassOfClass:[UILabel class]]) {
((UILabel *) view).backgroundColor = nil;
view.opaque = NO;
}
}
// regular stuff: only flash the selection, don't leave it blue forever
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
This appears to work, but I don't expect it to be a good idea forever. What is the Right Way to fix this?
This looks like a bug in UITableView's rendering, and you should file a Radar bug report on it. It's like the cells don't get refreshed properly after the move.
One way to work around this for now is to not use the built-in label, but roll your own in the cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
CGRect frame = cell.contentView.bounds;
frame.origin.x = frame.origin.x + 10.0f;
UILabel *textLabel = [[UILabel alloc] initWithFrame:frame];
[textLabel setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
textLabel.tag = 1;
textLabel.textAlignment = UITextAlignmentLeft;
textLabel.backgroundColor = [UIColor clearColor];
textLabel.textColor = [UIColor blackColor];
textLabel.font = [UIFont boldSystemFontOfSize:20.0];
textLabel.numberOfLines = 1;
textLabel.highlightedTextColor = [UIColor whiteColor];
[cell.contentView addSubview:textLabel];
[textLabel release];
}
UILabel *textLabel = (UILabel *)[cell viewWithTag:1];
textLabel.text = #"Test";
return cell;
}
I tried this, and it doesn't exhibit the same sort of white blank rectangle you see with the built-in label. However, adding another non-opaque view to the table cell might not be the best for overall rendering performance.
I don't know how major of a glitch this is, because Apple doesn't want you to persist a selection highlight on a table row (they've been enforcing this lately during the review process). You're supposed to place a checkmark or move on to the next level in the navigation hierarchy with a selection, at which point this white box would only be on the screen for a fraction of a second.
The trick in the solution from Brad appears to be:
textLabel.backgroundColor = [UIColor clearColor];
If you leave the background as the default you still get the problem even when you roll your own cells UITableViewCells.
The reason I left it as the default is because the documentation says it is less computationally costly to use opaque backgrounds. Ideally I wouldn't want to use [UIColor clearColor] to fix this bug.
Maybe a completely custom painted cell would somehow fix it. I haven't tried those before though.
Does anyone else have a solution for this?
Thanks for the info, I was searching how to erase the background color from a UILabel.
I used the following line:
textLabel.backgroundColor = [UIColor clearColor];
and worked perfectly!!!
thanks
Alejandra :)
Selections aren't meant to be shown for extended periods! (We got knocked on this for several of our apps)
??? That means Apple would not approve their own Calendar app on iPhone! When you go to edit the start and end times of the event, the start time is selected indefinitely, it only changes once the user taps to the next field.