Blurry UILabel as programmatic subview of UITableViewCell contentView - iphone

I am adding a UILabel instance as a subview of my custom UITableViewCell instance's contentView.
When I select the cell, the row is highlighted blue, except for the background of the label. The label text is sharp.
When I set the label and content view backgroundColor property to [UIColor clearColor], the label text becomes blurry.
How do I set the label background color to be clear, to allow the row highlight to come through, while still keeping the label text sharp?
One suggestion I read elsewhere was to round the label's frame values, but this did not have any effect.
CODE
Here is a snippet of my custom UITableViewCell subview's -setNeedsLayout method:
UILabel *_objectTitleLabel = [[UILabel alloc] initWithFrame:CGRectNull];
_objectTitleLabel.text = [self.awsObject cleanedKey];
_objectTitleLabel.font = [UIAppDelegate defaultObjectLabelFont];
_objectTitleLabel.highlightedTextColor = [UIColor clearColor]; //[UIAppDelegate defaultLabelShadowTint];
_objectTitleLabel.backgroundColor = [UIColor clearColor]; //[UIAppDelegate defaultWidgetBackgroundTint];
_objectTitleLabel.frame = CGRectMake(
kCellImageViewWidth + 2.0 * self.indentationWidth,
0.5 * (self.tableView.rowHeight - 1.5 * kCellLabelHeight) + kCellTitleYPositionNudge,
contentViewWidth,
kCellLabelHeight
);
_objectTitleLabel.frame = CGRectIntegral(_objectTitleLabel.frame);
_objectTitleLabel.tag = kObjectTableViewCellTitleSubviewType;
//NSLog(#"_objectTitleLabel: %#", NSStringFromCGRect(_objectTitleLabel.frame));
[self.contentView addSubview:_objectTitleLabel];
[_objectTitleLabel release], _objectTitleLabel = nil;
...
self.contentView.backgroundColor = [UIAppDelegate defaultWidgetBackgroundTint];
self.contentView.clearsContextBeforeDrawing = YES;
self.contentView.autoresizesSubviews = YES;
self.contentView.clipsToBounds = YES;
self.contentView.contentMode = UIViewContentModeRedraw;

The issue is sub-pixel rendering, which occurs when your origin (which is a float value) has a non-zero fractional component. Round to the nearest whole number and you should be fine.

In my case, having set shouldRasterize = YES on the CGLayer of the view containing the UILabel was the culprit. Removing that line made the text nice and crisp.

Ok found the problem, Make sure your parent view's coordinates are rounded as well.

I ran into this problem myself today, and read somewhere that non-integer values for the origin and size of the UILabel's frame can cause this (I know they're floats, but you know what I mean). There has got to be a more elegant solution, but this quick hack appears to have solved the problem for me:
self.valueLabel.frame = CGRectMake((int) frame.origin.x, (int) frame.origin.y, (int) frame.size.width, (int) frame.size.height);
If you find a better solution, please let me know, I'd love to replace this hack with something a bit more tasteful.

Another cause of garbled/blurry text is cell reuse. If you are de-queuing a reusable cell then it may redraw with different dimensions somewhere else and again be re-used when it gets to your cell with the garbled text.
To ensure the cells are unique be sure to allocate a new cell for the indicies where the text is garbled, and mark that UITableViewCell instance with a different reuse identifier. This is only practical of course if you're dealing with a very small number of cells and if you know exactly which cells are causing problems.

Setting shouldRasterize to YES may introduce blurriness. Set the rasterization scale and that should eliminate the blurriness. [self.layer setRasterizationScale:[[UIScreen mainScreen] scale]];

Sometimes the reason for the blurriness you have mentioned can be that labels's frame is beyond the cell frame.
Even if you see all of your text you have put inside the label on your cell, the actual label size can be bigger than the cell frame.
To check if that is the reason for the effect you see I would suggest to check/print all the data you have about labels size/location after it is instantiated and than check in the delegate method tableView:heightForRowAtIndexPath: that this fit into the cell height you are returning for the cell.
Hope it will help in your case.

Use round(); C functions are provided for a reason.
#define roundCGRectValues (frame) \
frame = CGRectMake(round(frame.origin.x),round(frame.origin.y),round(frame.size.width),round(frame.size.height));
All you need.

Does -setNeedsLayout get called even for dequeued reusable cells? If so, the cell will already have the label added to the content view, and you will draw it twice, making it blurry. You can inefficiently solve this by removing all of the content view's subviews before you add your subview:
for (UIView *subview in [[self contentView] subviews]) {
[subview removeFromSuperview];
}
A better solution would be to provide properties on your cell subclass to let you modify the content of a reused cell as-needed, rather than rebuilding its view hierarchy from scratch.

Related

How can I get the UITextView to scroll only when it's full of text

I have this UITextView that works great except, I can't get the text inside the UITextView to start scrolling only after the UITextView's size in nearly full, the UITextView is 4 lines tall, but as soon as I reach the 2nd line the 1st line is pushed up, I don't want the view to begin scrolling until I've reached the 5 line. scrollingEnabled = NO keeps it from scrolling at all, so that didn't work.
UITextView *barf_ = [[UITextView alloc] initWithFrame:CGRectMake(20.0, 310.0, 155, 50)];
barf_.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
//[barf_ scrollRangeToVisible:NSMakeRange([barf_.text length], 0)];
barf_.layer.cornerRadius = 3.0f;
barf_.layer.borderWidth = 0.5f;
barf_.font = [UIFont fontWithName:#"Helvetica" size:13];
I found the answer, as others with similar problems have mention, with a small textView, it automatically adds 32 padding to the bottom.
A simple fix is to add YourTextView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0); inside shouldChangeTextInRange method, that fixed my problem!
Setting the contentInset may help the text to appear more correctly within the UITextView. However, it won't help solve the issue whereby the UITextView has scrolling enabled despite not having more text to view.
Similarly, methods such as sizeWithFont have limitations. As explained in Mike Weller's excellent blog series iOS Development: You're Doing It Wrong, NSString isn't a good object to ask regarding how large a UIView should be. Many UIView subclasses such as UILabel, UIButton, etc. have insets and other considerations that must be accounted for during sizing. UITextLabel is no exception.
Mike Weller's particular entry on this subject is:
You're Doing It Wrong #2: Sizing labels with -[NSString sizeWithFont:...]
iOS 7 promises us more sophisticated text handling in UITextView, with properties such as textContainerInset. But what to do in the meantime?
Well, first we know that UITextView is a subclass of UIScrollView. Therefore, the golden rule that if the contentSize is larger than the view's bounds property, the scroll view will scroll so we can see more content.
Checking out contentSize agains the bounds won't work either because we know that UIScrollView is already calculating whether it should scroll or not based on the text, and it's giving us the wrong answer.
This is where arbitrary adjustment values come to the rescue! For me this value was 17.f. For you - depending on your fonts - it maybe different. We then take control and decide whether we should allow the scroll view to scroll:
static const CGFloat kArbritaryHeight = 17.f;
CGFloat adjustedContentHeight = myTextView.contentSize.height - kArbritaryHeight;
CGFloat boundsHeight = CGRectGetHeight(myTextView.bounds);
BOOL tooMuchContent = adjustedContentHeight > boundsHeight;
if (tooMuchContent)
{
myTextView.scrollEnabled = YES;
}
else
{
myTextView.scrollEnabled = NO;
}
When your UITextView is loaded set scrollEnabled to NO. Then set the text view's delegate to self or some other object and implement the UITextViewDelegate method
- (void)textViewDidChange:(UITextView *)textView
This method will get called anytime the user makes a change to the text inside the view. Inside this method you need to figure out how big your text is and if it goes beyond the bounds of the text view. If so you enable scrolling. Use this method:
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
This is a UIKit category method on NSString. It returns a CGSize that will tell you the height of whatever text string you call it on. In your case it would be something like
CGSize textSize = [textView.text sizeWithFont:textView.font
constrainedToSize:CGSizeMake(textView.frame.size.width, MAXFLOAT)
lineBreakMode:UILineBreakModeWordWrap];
if (textSize.height > textView.frame.size.height) {
textView.scrollEnabled = YES;
} else {
textView.scrollEnabled = NO;
}
You might use the sizeWithFont:constrainedToSize:lineBreakMode: method to check whether your string will actually render larger than your text view and see if you need to enable scrolling. You will have to call it any time the text in your scrollview is set, however.
ex:
CGSize barfStringSize = [barfString sizeWithFont:[barf_ font]
constrainedToSize:CGSizeMake(barf_.bounds.size.width, MAXFLOAT)
lineBreakMode:UILineBreakModeWordWrap]
[barf_ setScrollEnabled:barfStringSize.height > barf_.bounds.size.height]

Why I cant be able to change the UITableViewCell detailTextLabel's frame (just the label's position)?

I am having a table view with default UITableViewCell of style UITableViewCellStyleValue2. I just want to move detailTextLabel few pixels to the right. I know it makes no sense to adjust its width and height :). I am trying to set detailTextLabel's frame with my x and y value. But its not affecting the its frame. I prefer to use default UITableViewCell, in this case, over a customized cell because the default UITableViewCell automatically manages the text alignment and centering of the labels..
How to change UITableViewCell detailTextLabel's frame?
Am I allowed to change its frame?
Thanks everyone..
You said you want to change the frame of detailTextLabel without using the custom cell implementation then the answer to your questions will be :-
It is not possible
No, it is not possible
For that you have to use custom cell , and add probably a textview or a label to the cell's contentView according to your needs
They have the default size with the table view cell.You can do the custom implementaion.Here is the example I am giving to you
UILabel *productNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(105, 35, 60,20 )];
productNameLabel.textColor = [UIColor whiteColor];
productNameLabel.backgroundColor=[UIColor clearColor];
productNameLabel.text=#"Name";
[cell.contentView addSubview:productNameLabel];
[productNameLabel release];
Hope this will help you.Try to use the custom implementaion.

iPhone UITableViewCell: repositioning the textLabel

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
}

Best way to add a large chunk of text to a UIScrollView?

What is the best way to display a large chunk of text (taken from a .txt file in the app) in a UIScrollView so a user can scroll about it? Length of the text is variable.
On Interface Builder open the Attributes Inspector (if not already open - command-1) and uncheck "Editable".
Also notice there's a Scroll View section below. Make sure "Scrolling" is checked.
Hope this helps somebody (the post is a year old so I guess by now the one who posted it doesn't need this info).
I came here looking for an answer and found that all answers are bad - or flat out wrong.
The proper way to do this is using UITextView by itself. Since it is a descendant of UIScrollView, it has scrolling built-in and lots of features for adjusting formatting such as the insets etc.
If you intend to only show text, you need to explicitly disable editing. You do this by setting the "editable" property to false.
And if you want to disable the text selection mechanism, set the "selectable" property to false.
In newer versions of iOS, UITextView has added support for NSTextContainer which gives you even greater control over formatting.
One way I had working for me is to create UILabel, set text and then set content size of scrollview by it size.
Here is an example
Quote:
// alocate and initialize scroll
UIScrollView *myScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
// alocate and initialize label
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
// add long text to label
myLabel.text = #"Lorem ipsum... long text here";
// set line break mode to word wrap
myLabel.lineBreakMode = UILineBreakModeWordWrap;
// set number of lines to zero
myLabel.numberOfLines = 0;
// resize label
[myLabel sizeToFit];
// set scroll view size
myScroll.contentSize = CGSizeMake(myScroll.contentSize.width, myLabel.frame.size.height);
// add myLabel
[myScroll addSubview:myLabel];
// add scroll view to main view
[self.view addSubview:myScroll];
Usage of the UITextView into the UIScrollView. I could not recommend this because UITextView is the subclass of UIScrollView. Apple is also recommending the same.
Use UILabel in this case as a sub-view,
Put the UITextView into the UIScrollView.

Light gray background in "bounce area" of a UITableView

Apple's iPhone apps such as Music and Contants use a search bar in a UITableView. When you scroll down so that the search bar moves down, the empty space above the scroll view's contents has a light gray background color (see screenshot).
(Notice that the search bar has a slightly darker edge line at its top. This is not there for the default UISearchBar, but subclassing should take care of that.)
I tried setting the background color of the UITableView, but that affects the rows as well. Does anyone know how to achieve this effect? Am I going to have to override implement drawRect: or is there a built in way?
Setting transparencies is bad for performance. What you want is the gray area above the search bar, but it should still be white beyond the end of the list.
You can add a subview to your UITableView that lives above the content instead.
CGRect frame = self.list.bounds;
frame.origin.y = -frame.size.height;
UIView* grayView = [[UIView alloc] initWithFrame:frame];
grayView.backgroundColor = [UIColor grayColor];
[self.listView addSubview:grayView];
[grayView release];
You could add more fancy stuff to the view if you like, perhaps a fade, or a divider line without subclassing UISearchBar.
This is one of my very favorite tricks.
UIView *topview = [[[UIView alloc] initWithFrame:CGRectMake(0,-480,320,480)] autorelease];
topview.backgroundColor = [UIColor colorWithRed:226.0/255.0 green:231.0/255.0 blue:238.0/255.0 alpha:1];
[self.tableView addSubview:topview];
Basically you're creating a big view the size of the screen and placing it "above" the content area. You'll never be able to scroll up past it.
And don't worry about the memory impact of a UIView that's 320x480 pixels, it won't consume any significant memory because the CALayer doesn't have any meaningful content.
NOTE: Why is this answer relevant when the "accepted" answer is so much simpler? Why not just set the backgroundView on the table view? It's because, in the case of the Contacts app as shown in the original question, the area "above" the table view has a different background color (light blue) than the area "below" the table view (white). This technique allows you to have two different colors above and below the table view, which cannot be accomplished by a simple background.
EDIT 1/2018: As Tom in the comments pointed out, this answer is quite old and assumes that all iOS devices have the same screen size (seems crazy but it was the case in 2009 when I answered this). The concept I present here still works, but you should use UIScreen.main.bounds to figure out the actual screen size, or you could get into some fancy auto layout stuff (suggestions welcome). I don't recommend using tableView.bounds as in another answer, because typically in viewDidLoad the size of your views is not necessarily the size that they will become after the controller resizes them. Sometimes they start out as 0x0!
To extend on HusseinB's suggestion:
Swift 3
let bgView = UIView()
bgView.backgroundColor = UIColor.white
self.tableView.backgroundView = bgView
Objective C
UIView *bgView = [UIView new];
bgView.backgroundColor = [UIColor whiteColor];
[self.tableView setBackgroundView:bgView];
As of iOS 7, you can tinker this by changing the tableview background view.
[self.tableView setBackgroundView:view];
make the view's background colour the same as your parent view colour.
This code works in Swift fot UITableView:
var frame = self.tableView.bounds
frame.origin.y = -frame.size.height
frame.size.height = frame.size.height
frame.size.width = UIScreen.mainScreen().bounds.size.width
let blueView = UIView(frame: frame)
blueView.backgroundColor = UIColor.headerBlueColor()
self.tableView.addSubview(blueView)
In Swift (tested on iOS9)
let backView = UIView(frame: self.tableView.bounds)
backView.backgroundColor = UIColor.clearColor() // or whatever color
self.tableView.backgroundView = backView
EASIEST SOLUTION
The easiest way to create different colors in the bottom and in the top of a bouncing area of a table view is to set the key tableHeaderBackgroundColor of the table view. Doing this way you set the top color. I'm not sure, but maybe there is another key for the footer, take a look. If you don't find anything, you just have to set the background of the table view with the color that you want to show in the bottom. Above you can see an example code:
self.table.setValue(UIColor.blue , forKey: "tableHeaderBackgroundColor")
Hope it help you. If yes, let other people know about this easy way giving an up in the answer :)
I've only found one way to do this. You have to set the backgroundColor of the UITableView to be transparent, set the backgroundColor of the cell's contentView to whatever colour you want the actual cells to be, then crucially you have to get the light grey colour to appear behind the UITableView. That last step you can do by either setting the backgroundColour of the UIWindow, or of whatever is containing or your tableViewController.
So, assuming you have a view controller that is derived from UITableViewController, insert these lines in the -(void)viewDidLoad method:-
// sets the background of the table to be transparent
self.tableView.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.0];
// assuming we are inside a navigation or tab controller, set the background
self.parentViewController.view.backgroundColor = [UIColor lightGrayColor];
Then inside the part of tableView:cellForRowAtIndexPath: that creates new cells, add:-
// set an opaque background for the cells
cell.contentView.backgroundColor = [UIColor whiteColor];
I just encountered this issue myself and found a solution.
Cause
I used Spark Inspector to examine the layout of the table view - which really helped.
Thing is, that in this scenario the UITableView has 3 subviews:
UITableViewWrapperView
UIView - With backgroundColor set to light gray color
UISearchBar
While you swipe the tableview content downwards, the second subview height is dynamically increasing to fill the space between the UITableViewWrapperView and the UITableView frame origin.
Solution
Setting the backgroundColor or backgroundView property won't effect the 2nd subview.
What you need to do is find the second view and change its color, like so:
if (_tableView.subviews.count > 1) {
_tableView.subviews[1].backgroundColor = THE_TARGET_COLOR;
}
In my case I needed all views to be white so I used the following which is less prone to future changes of UITableView view hierarchy by Apple:
for (UIView *subview in _tableView.subviews) {
subview.backgroundColor = [UIColor whiteColor];
}
I will give you the best way to do this.
First set the background color of the table view to the one you want in interface builder.
Then respond to the UITableView delegate tableView:willDisplayCell:ForIndexPath: method
like this
- (void)tableView:(UITableView*)tableView willDisplayCell:(UITableViewCelll*)cell forIndexPath:(NSINdexPath*)indexPath
{
[cell setBackgroundColor:[UIColor whiteColor]];
}
Another Method is :
in ViewDidLoad method (or anywhere you like) set the tableView background color to clear color like this:
self.tableView.backgroundColor = [UIColor clearColor];
and then set the superview color to white
self.tableView.superview.backgroundColor = [UIColor whiteColor];
I don't think you want to override drawRect. Most likely what you're seeing is the background colour of another view or the window, which lies "behind" (i.e. is a superview of) the table view. There's usually a fairly complex layers of UIViews in Apple's UI widgets. Explore the view hierarchy in GDB, look at [myView superview] and then [someSuperView subviews] and try manipulating their BG colours in the debugger to see if you can find which one it is. However, if you implement a fix this way, be warned that it may not be future compatible.
You might also try setting the BG colour of one of the views behind the tableview in Interface Builder (or of the window itself).
If you are using a tableviewcell, you can set the view background to be opaque white. Then use
self.tableView.backgroundColor = [UIColor grayColor];
in the view did load method.
I'm sure that that is [UITableView backgroundColor].
You have affected rows, because rows have backgroundColor == clear (or semi-transparent).
So, If you'll make rows non-trasparent, all will work fine.
This will be solution.
I followed the tip outlined by Peylow, for a UITableView, by simply adding a subview. My only change from the code was to grab a color a bit closer to the one used in Apple apps, plus I got it a bit closer to Apple's look of having a line above the UISearchbar by reducing the frame origin y coordinate by one pixel:
frame.origin.y = -frame.size.height - 1
For anyone who's wondering how to do the same for the bottom bounce area:
First add a subview with your desired background color to your table view's background view:
self.bottomView = [[UIView alloc] initWithFrame:CGRectOffset(self.tableView.frame, 0, self.tableView.frame.size.height)];
self.bottomView.backgroundColor = whateverColorYouLike;
[self.tableView.backgroundView addSubview:self.bottomView];
And then in your table view's delegate:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect frame = self.bottomView.frame;
frame.origin.y = self.tableView.contentSize.height - self.tableView.contentOffset.y;
self.bottomView.frame = frame;
}
In my case the solution was to create a headerview for the table and assign a color, it solved the black background in bounce area in my apps when in dark mode. I did the same to its tableFooterView.
table.tableHeaderView = UIView()
table.tableHeaderView!.backgroundColor = UIColor.white