When drawing strings using drawAtPoint:, drawInRect: and even setting the text property of UILabels - the text can sometimes appear slightly blurry.
I tend to use Helvetica in most places, and I notice that specific font sizes cause some level of blurriness to occur, both in the simulator and on the device.
For example:
UIFont *labelFont = [UIFont fontWithName:#"Helvetica-Bold" size:12];
Will cause the resulting label to have slightly blurry text.
UIFont *labelFont = [UIFont fontWithName:#"Helvetica-Bold" size:13];
Results in crisp text.
My question is why does this occur? And is it just a matter of selecting an optimal font size for a typeface? If so, what are the optimal font sizes?
UPDATE: It seems that perhaps it is not the font size that is causing the blurriness. It may be that the center of the rect is a fractional point. Here is a comment I found on the Apple dev forums:
Check the position. It's likely on a
fractional pixel. Change center to be
integer value.
I rounded off the values of all my points, but there are still places where text remains blurry. Has anyone come across this issue before?
I have resolved this.
Just make sure that the point or rect in which you are drawing does not occur on a fractional pixel.
I.e. NSLog(#"%#", NSStringFromCGRect(theRect)) to determine which point is being drawn on a fractional pixel. Then call round() on that point.
You might want to look at NSIntegralRect(), it does what you want.
Pardon my ignorance if this is incorrect, I know nothing about iPhone or Cocoa.
If you're asking for the text to be centered in the rect, you might also need to make sure the width and/or height of the rect is an even number.
I have had this problem too, but my solution is different and may help someone.
My problem was text blur after changing the size of a UIView object thru TouchesBegan
and CGAffineTransformMakeScale, then back to CGAffineTransformIdentity in TouchesEnded.
I tried both the varying text size and rounding of x and y center points but neither worked.
The solution for my problem was to use even sizes for the width and height of my UIView !!
Hope this helps ....
From my experiments, some fonts don't render clearly at certain sizes. e.g. Helvetica-Bold doesn't render "." well at 16.0f, but they're clear at 18.0f. (If you look closely, the top pixel of the "." is blurry.)
After I noticed that, I've been irked every time I see that UIView, since it's rendered dynamically.
In my case, I drew text on a custom CALayer and turned out to be pretty blurry. I solved it by setting contentScale with appropriate value:
Objective-C:
layer.contentsScale = [UIScreen mainScreen].scale;
Swift:
layer.contentsScale = UIScreen.main.scale
Related
The short version: How do I know what region of a UIImageView contains the image, and not aspect ratio padding?
The longer version:
I have a UIImageView of fixed size as pictured:
I am loading photos into this UIViewController, and I want to retain the original photo's aspect ratio so I set the contentMode to Aspect Fit. This ends up ensuring that the entire photo is displayed within the UIImageView, but with the side effect of adding some padding (configured in red):
No problem so far.... But now I am doing face detection on the original image. The face detection code returns a list of CGRects which I then render on top of the UIImageView (I have a subclassed UIView and then laid out an instance in IB which is the same size and offset as the UIImageView).
This approach works great when then photo is not padded out to fit into UIImageView. However if there is padding, it introduces some skew as seen here in green:
I need to take the image padding into account when rendering the boxes, but I do not see a way to retrieve it.
Since I know the original image size and the UIImageView size, I can do some algebra to calculate where the padding should be. However it seems like there is probably a way to retrieve this information, and I am overlooking it.
I do not use image views often so this may not be the best solution. But since no one else has answered the question I figured I'd through out a simple mathematical solution that should solve your problem:
UIImage *selectedImage; // the image you want to display
UIImageView *imageView; // the imageview to hold the selectedImage
NSInteger heightOfView = imageView.frame.size.height;
NSInteger heightOfPicture = selectedImage.size.height;
NSInteger yStartingLocationForGreenSquare; // set it to whatever the current location is
// take whatever you had it set to and add the value of the top padding
yStartingLocationForGreenSquare += (heightOfView - heightOfPicture) / 2;
So although there may be other solutions this is a pretty simple math formula to accomplish what you need. Hope it helps.
I'm drawing multiple 'pages' of imagery using a CGContext in the iOS. I've used sizeWithFont and drawInRect combinations extensively in my app. What I need to do is split a large chunk of text across multiple pages. I can size it and work out whether or not it needs another page but how do I know where to chop it? Do I have to do an ugly loop to check word by word until I find a string length that perfectly fits the page and then chop the string at that point? Is there a smarter way?
Any ideas?
Thanks.
The NSString additions to UIKit for drawing text, you can pre-determine the exact amount of space required to render a given text for a given font. If splitting the text into pages, you could use this method.
– sizeWithFont:constrainedToSize:lineBreakMode:
Assuming the font and line break mode is known, create a CGSize having the same width as your page, and use a sufficiently number for height. This would be the maximum size that we are constraining the text into.
CGSize maximumSize = CGSizeMake(pageWidth, 999999999);
CGSize expectedSize = [veryLongString sizeWithFont:theFont constrainedToSize:maximumSize lineBreakMode:theLineBreakMode];
expectedSize will tell us the size that the text will take assuming if it could extend vertically infinitely (well close to). To find the number of pages required, just divide the total height by the height of one page.
NSInteger totalPages = ceil(expectedSize.height / heightOfOnePage);
You would also want to adjust the height of one page to make sure that the last line of text doesn't get clipped. For that to happen, the height of the page should be a multiple of the font's line height. Say the initial page height is 300px, and the font-height is 16px, then there will be some clipping as 300/16 = 18.75 which is not a whole number.
NSInteger linesWithoutClipping = floor(initialPageHeight / theFont.lineHeight);
CGFloat optimalPageHeight = linesWithoutClipping * theFont.lineHeight;
Taking the floor value 18 and multiplying with the font line height 16, we get an optimal page height of 288 to ensure there's no clipping.
Note that lineHeight was introduced in iOS 4.0, but you could calculate it yourselves if needed for older versions.
The way I get around this problem is to split by line returns.
NSArray * paragraphs = [text componentsSeparatedByString:#"\n"];
You still have to do all the work to determine page breaks and alike but I have found this the best workaround so far.
I have to show a label with font size = 14
when the view is 480x320.
Supposing I would like to do this in a way that my code will be working well for all future device's screen sizes, including iPad and others following (I am sure more will come), what is the best way to do that?
I could do this proportionally, I mean, if the scale increased X, increase the fonts X, but my concern is the different aspect ratios of the devices.
The iPhone aspect ratio is 1.5, but iPad's is 1.33, and other aspect ratios can come... I am not sure if this simple scale method will be enough to produce font consistency across all devices.
Any suggestions?
thanks for any help.
Simply setting uiLabel.adjustsFontSizeToFitWidth = YES will cause your text to scale down to fit the text field. Just make sure your font size is large enough for the iPad to begin with, because the text will never be scaled up.
Of course, you also need to make sure the UILabel itself is resized along with its superview, or neither the UILabel nor its contents will be scaled. You can do that using the Size tab in Interface Builder, or programatically using the UILabel's autoresizingMask property.
You could try to use this code. It will automatically scale your text down to fit within a certain width (here the contentRect). Just specify the minimum and maximum acceptable font size to use:
#define MAX_FONT_SIZE
#define MIN_FONT_SIZE
#define LEFT_TEXT_OFFSET
#define TEXT_TOP
UIFont *theFont = [UIFont systemFontOfSize: MAX_FONT_SIZE];
CGRect contentRect = self.bounds;
CGFloat boundsX = contentRect.origin.x;
CGPoint point;
CGSize width = contentRect.size.width;
point = CGPointMake(boundsX + LEFT_TEXT_OFFSET, TEXT_TOP);
[theText drawAtPoint:point forWidth:width withFont:theFont
minFontSize:MIN_FONT_SIZE actualFontSize:NULL
lineBreakMode:UILineBreakModeTailTruncate
baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
Claus
I have two UILabels. that I want to overlap one atop the other. Call the labels "under" and "over".
over: A C E G
under: B D F
UILabel "over" will have its text drawn in red. "under" will be in blue. The visual effect will be alternating colors between successive letters.
What are the controls available to me to exactly align the text in each label to pull this off?
Cheers,
Doug
I agree with fbrereton. This seems a very hard way to achieve the goal. Check out the NSString UIKit Additions to learn how to draw your own strings and lay the characters out yourself in your own custom -drawRect:. You'll have far greater control, and the code should not be that complex. iPhone doesn't have very good layout support (nothing like Mac offers), but for something this simple, it shouldn't be too bad.
You have to make sure the font you are using is a monospace font, otherwise the characters will not line up exactly as you would hope with just a space between them. (I believe there is a typewrite font available in the iPhone OS that is monospace; YMMV.) Also you will have to prefix underLabel with a space for the code below to work.
To map one UILabel on top of another, try:
overLabel.opaque = NO; // so you can see what is under overLabel
overLabel.textColor = [UIColor redColor];
overLabel.backgroundColor = [UIColor clearColor];
underLabel.frame = overLabel.frame;
underLabel.textColor = [UIColor blueColor];
Note in the above code underLabel takes on overLabel's frame because the latter's frame is wider; if it were the other way around overLabel would get clipped.
All that being said I'd wager there is a better way to skin this particular cat. This solution feels very "round peg, square hole" to me.
I have an NSString and want to fit it into a rectangle. The rectangle has a specified size, lets say width=150 and height=30. When the String is short and has only one character, it can be as high as the rectangle. More specific: It can have a big font size. But if the string has too much characters and would exceed the bounds of the rectangle, it must become smaller. More specific: It's font size must become smaller, so that it won't exceed the bounds of the rectangle. Is there a way of doing that without messing around in core graphics?
For some reason, UILabel's adjustsFontSizeToFitWidth Property has no effect. The text keeps beeing small even if there is plently of space.
I've set that to
label.adjustsFontSizeToFitWidth = YES;
but nothing happens. I hope there is another way to do that...
There are several part to UILabel that make this possible, but first you have to know if you want to truncate the string or make the font size smaller to fit in the rectangle.
For both cases you'll want to set the UILabel's numberOfLines property to 0, allowing the label to wrap as much as necessary. Then you'll want to set the frame of the UILabel to match the rectangle you're looking to fit. From there you take one of two paths:
Truncation: Set the lineBreakMode property to UILineBreakModeClip, UILineBreakModeHeadTruncation, UILineBreakModeTailTruncation, or UILineBreakModeMiddleTruncation depending on the truncation behavior you're looking for.
Resizing: Set the lineBreakMode to either UILineBreakModeWordWrap or `UILineBreakModeCharacterWrap' depending on your preference. Then you'll need to enter a loop to figure out the right font size. Start with a reasonable font size (e.g., 12) and:
Set the font property of the UILabel with a UIFont that matches that size
Call - (void) sizeToFit for the UILabel.
Check the frame for the UILabel:
If the frame will fit within the bounds you need it to, you're done
If the frame is still to big, drop the size of the font and repeat the loop
For the latter option you'll want to make sure you're not squeezing the text into oblivion, so you'll want to put a minimum size cap on the font size.
You can get more information from the UILabel and UIFont documentation.
adjustsFontSizeToFitWidth adjusts the font size down, not up.
Set the font on your UILabel to an appropriately large size, and UILabel will shrink it when necessary to fit in its bounds.
Set label font size to desired normal size.
Set label minimum font size to
smallest possible font
(minimumFontSize property)
Set adjustsFontSizeToFitWidth to
YES.
If you need multiple lines, set lineCount to 0 as the other poster noted.