Scale NSString Font to UIImage Size - xcode

I'm writing text onto a image taken from the iDevice's camera or chosen from the photo library but I need the fontsize scaled according to width/height of the image. Here's my current code:
UIGraphicsBeginImageContext(img.size);
CGRect aRectangle = CGRectMake(0,0, img.size.width, img.size.height);
[img drawInRect:aRectangle];
[[UIColor whiteColor] set]; // set text color
NSInteger fontSize = 45;
UIFont *font = [UIFont systemFontOfSize:fontSize];// set text font
[ text drawInRect : aRectangle // render the text
withFont : font
lineBreakMode : UILineBreakModeTailTruncation // clip overflow from end of last line
alignment : UITextAlignmentCenter ];
UIImage *theImage=UIGraphicsGetImageFromCurrentImageContext(); // extract the image
UIGraphicsEndImageContext(); // clean up the context.
return theImage;</i>

You want something like this. It creates a rect half the size of your image, then uses a binary search to zoom in on the font size you want.
//---- initial data
CGSize szMax = CGSizeMake(<imgWidth> / 2, <imgHeight> / 2);
CGFloat fMin = 2; //-- the smallest size font we want
CGFloat fMax = 100; //-- the largest size font we want
CGFloat fMid; //-- the middle of min and max
while ((fMax - fMin) >= 1.0) //-- repeat until the sizes converge
{
fMid = (fMin + fMax) / 2.0; //-- compute mid-point
UIFont *pfnt = [UIFont systemFontOfSize: fMid]; //-- create middle-sized font
//---- compute the size of the text using the mid-sized font
CGSize szStr = [pStr sizeWithFont: pfnt constrainedToSize: szMax lineBreakMode: UILineBreakModeWordWrap];
if (szStr.height > szMax.height)
fMax = fMid; //-- text too tall, set max to mid-point
else
fMin = fMid; //-- text too short, set min to mid-point
}
//---- 'fMid' is the size you want.

Related

calculate height and width based on text , font , text color in xcode

what is the best way to calculate (formated text including text size , text color , text font) proper height and width of text field in a fixed UIView , including word wrap etc
in the following image text is not right align Check the Image
in the following image text is not properly showing check the image
i am calculating image height and width with the following code
NSString* text=[textField stringValue];
NSDictionary *attributes;
NSTextView* textView =[[NSTextView alloc] init];
[textView setString:text];
attributes = #{NSFontAttributeName : [NSFont fontWithName:fontName size:fontValue], NSForegroundColorAttributeName : [NSColor colorWithCalibratedRed:redValueTextColor green:GreenValueTextColor blue:blueValueTextColor alpha:1], NSBackgroundColorAttributeName : [NSColor colorWithCalibratedRed:redValueTextBackgroundColor green:GreenValueTextBackgroundColor blue:blueValueTextBackgroundColor alpha:1]};
textView.backgroundColor=[NSColor colorWithCalibratedRed:redValueTextBackgroundColor green:GreenValueTextBackgroundColor blue:blueValueTextBackgroundColor alpha:1];
NSInteger maxWidth = 600;
NSInteger maxHeight = 20000;
CGSize constraint = CGSizeMake(maxWidth, maxHeight);
NSRect newBounds = [text boundingRectWithSize:constraint options:NSLineBreakByCharWrapping|NSStringDrawingUsesFontLeading attributes:attributes];
textView.frame = NSMakeRect(textView.frame.origin.x, textView.frame.origin.y, newBounds.size.width, newBounds.size.height);
textView =[NSColor colorWithCalibratedRed:redValueTextColor green:GreenValueTextColor blue:blueValueTextColor alpha:1];
[textView setFont:[NSFont fontWithName:fontName size:fontValue]];
Core Text solution (note, maxWidth allows wrapping if you want it):
(CGSize)sizeForText:(NSAttributedString *)string maxWidth:(CGFloat)width
{
CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((__bridge CFAttributedStringRef)string);
CFIndex offset = 0, length;
CGFloat y = 0, lineHeight;
do {
length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));
CGFloat ascent, descent, leading;
CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
CFRelease(line);
offset += length;
ascent = roundf(ascent);
descent = roundf(descent);
leading = roundf(leading);
lineHeight = ascent + descent + leading;
lineHeight = lineHeight + ((leading > 0) ? 0 : roundf(0.2*lineHeight)); //add 20% space
y += lineHeight;
} while (offset < [string length]);
CFRelease(typesetter);
return CGSizeMake(width, y);
}

UIImageview programmilly manage for iphone5 and iphone4

I have an Issue about UIImageView Manage by XIB file for iphone5 screen Height and Iphone4 Screen Height.
I Try to Manage code for UIImageView like this
~
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
if ([UIScreen mainScreen].scale == 2.f && screenHeight == 568.0f) {
backgroundImage.autoresizingMask=UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
frameView.autoresizingMask=UIViewAutoresizingFlexibleHeight;
backgroundImage.image = [UIImage imageNamed:#"bg-568h#2x.png"];
//frameView.frame=CGRectMake(16, 0, 288, 527);
frameView.image = [UIImage imageNamed:#"setframe-568h#2x.png"];
}
else
{
backgroundImage.image = [UIImage imageNamed:#"bg#2x.png"];
frameView.image = [UIImage imageNamed:#"setframe#2x.png"];
} ;
Please suggest me about Issues, FrameView is a UIImageView which have white Image,
Please
THanks
I had the same issue and below is what I did to make it work for me.
I have images used in a couple of apps which needed to be resized for the new 4 inch display. I wrote the code below to automatically resize images as needed without specifics on the height of the view. This code assumes the height of the given image was sized in the NIB to be the full height of the given frame, like it is a background image that fills the whole view. In the NIB the UIImageView should not be set to stretch, which would do the work of stretching the image for you and distort the image since only the height changes while the width stays the same. What you need to do is adjust the height and the width by the same delta and then shift the image to the left by the same delta to center it again. This chops off a little on both sides while making it expand to the full height of the given frame.
I call it this way...
[self resizeImageView:self.backgroundImageView intoFrame:self.view.frame];
I do this in viewDidLoad normally if the image is set in the NIB. But I also have images which are downloaded at runtime and displayed that way. These images are cached with EGOCache, so I have to call the resize method either after setting the cached image into the UIImageView or after the image is downloaded and set into the UIImageView.
The code below does not specifically care what the height of the display is. It actually could work with any display size, perhaps to handle resizing images for rotation as well, thought it assumes each time the change in height is greater than the original height. To support a greater width this code would need to be adjusted to respond to that scenario as well.
- (void)resizeImageView:(UIImageView *)imageView intoFrame:(CGRect)frame {
// resizing is not needed if the height is already the same
if (frame.size.height == imageView.frame.size.height) {
return;
}
CGFloat delta = frame.size.height / imageView.frame.size.height;
CGFloat newWidth = imageView.frame.size.width * delta;
CGFloat newHeight = imageView.frame.size.height * delta;
CGSize newSize = CGSizeMake(newWidth, newHeight);
CGFloat newX = (imageView.frame.size.width - newWidth) / 2; // recenter image with broader width
CGRect imageViewFrame = imageView.frame;
imageViewFrame.size.width = newWidth;
imageViewFrame.size.height = newHeight;
imageViewFrame.origin.x = newX;
imageView.frame = imageViewFrame;
// now resize the image
assert(imageView.image != nil);
imageView.image = [self imageWithImage:imageView.image scaledToSize:newSize];
}
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

How can I calculate (without search) a font size to fit a rect?

I want my text to fit within a specific rect, so I need something to determine a font size. Questions have already tackled this to an extent, but they do a search, which seems horribly inefficient, especially if you want to be able to calculate during a live dragging resize. The following example could be improved to binary search and by constraining to the height, but it is still a search. Instead of searching, how can I calculate a font size to fit a rect?
#define kMaxFontSize 10000
- (CGFloat)fontSizeForAreaSize:(NSSize)areaSize withString:(NSString *)stringToSize usingFont:(NSString *)fontName;
{
NSFont * displayFont = nil;
NSSize stringSize = NSZeroSize;
NSMutableDictionary * fontAttributes = [[NSMutableDictionary alloc] init];
if (areaSize.width == 0.0 && areaSize.height == 0.0)
return 0.0;
NSUInteger fontLoop = 0;
for (fontLoop = 1; fontLoop <= kMaxFontSize; fontLoop++) {
displayFont = [[NSFontManager sharedFontManager] convertWeight:YES ofFont:[NSFont fontWithName:fontName size:fontLoop]];
[fontAttributes setObject:displayFont forKey:NSFontAttributeName];
stringSize = [stringToSize sizeWithAttributes:fontAttributes];
if (stringSize.width > areaSize.width)
break;
if (stringSize.height > areaSize.height)
break;
}
[fontAttributes release], fontAttributes = nil;
return (CGFloat)fontLoop - 1.0;
}
Pick any font size and measure the text at that size. Divide each of its dimensions (width and height) by the same dimension of your target rectangle, then divide the font size by the larger factor.
Note that the text will measure on one line, since there is no maximum width for it to wrap to. For a long line/string, this may result in an unusefully-small font size. For a text field, you should simply enforce a minimum size (such as the small system font size), and set the field's truncation behavior. If you intend to wrap the text, you'll need to measure it with something that takes a bounding rectangle or size.
Code by asker roughly based on this idea:
-(float)scaleToAspectFit:(CGSize)source into:(CGSize)into padding:(float)padding
{
return MIN((into.width-padding) / source.width, (into.height-padding) / source.height);
}
-(NSFont*)fontSizedForAreaSize:(NSSize)size withString:(NSString*)string usingFont:(NSFont*)font;
{
NSFont* sampleFont = [NSFont fontWithDescriptor:font.fontDescriptor size:12.];//use standard size to prevent error accrual
CGSize sampleSize = [string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:sampleFont, NSFontAttributeName, nil]];
float scale = [self scaleToAspectFit:sampleSize into:size padding:10];
return [NSFont fontWithDescriptor:font.fontDescriptor size:scale * sampleFont.pointSize];
}
-(void)windowDidResize:(NSNotification*)notification
{
text.font = [self fontSizedForAreaSize:text.frame.size withString:text.stringValue usingFont:text.font];
}

Creating a UIImage from a rotated UIImageView

I have a UIImageView with an image in it. I have rotated the image prior to display by setting the transform property of the UIImageView to CGAffineTransformMakeRotation(angle) where angle is the angle in radians.
I want to be able to create another UIImage that corresponds to the rotated version that I can see in my view.
I am almost there, by rotating the image context I get a rotated image:
- (UIImage *) rotatedImageFromImageView: (UIImageView *) imageView
{
UIImage *rotatedImage;
// Get image width, height of the bounding rectangle
CGRect boundingRect = [self getBoundingRectAfterRotation: imageView.bounds byAngle:angle];
// Create a graphics context the size of the bounding rectangle
UIGraphicsBeginImageContext(boundingRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// Rotate and translate the context
CGAffineTransform ourTransform = CGAffineTransformIdentity;
ourTransform = CGAffineTransformConcat(ourTransform, CGAffineTransformMakeRotation(angle));
CGContextConcatCTM(context, ourTransform);
// Draw the image into the context
CGContextDrawImage(context, CGRectMake(0, 0, imageView.image.size.width, imageView.image.size.height), imageView.image.CGImage);
// Get an image from the context
rotatedImage = [UIImage imageWithCGImage: CGBitmapContextCreateImage(context)];
// Clean up
UIGraphicsEndImageContext();
return rotatedImage;
}
However the image is not rotated about its centre. I have tried all kinds of transforms concatenated with my rotate to get it to rotate around the centre but to no avail. Am I missing a trick? Is this even possible since I am rotating the context not the image?
Getting desperate to make this work now, so any help would be appreciated.
Dave
EDIT: I've been asked several times for my boundingRect code, so here it is:
- (CGRect) getBoundingRectAfterRotation: (CGRect) rectangle byAngle: (CGFloat) angleOfRotation {
// Calculate the width and height of the bounding rectangle using basic trig
CGFloat newWidth = rectangle.size.width * fabs(cosf(angleOfRotation)) + rectangle.size.height * fabs(sinf(angleOfRotation));
CGFloat newHeight = rectangle.size.height * fabs(cosf(angleOfRotation)) + rectangle.size.width * fabs(sinf(angleOfRotation));
// Calculate the position of the origin
CGFloat newX = rectangle.origin.x + ((rectangle.size.width - newWidth) / 2);
CGFloat newY = rectangle.origin.y + ((rectangle.size.height - newHeight) / 2);
// Return the rectangle
return CGRectMake(newX, newY, newWidth, newHeight);
}
OK - at last I seem to have done it. Any comments on the correctness would be useful... needed a translate, a rotate, a scale and an offset from the drawing rect position to make it work. Code is here:
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, boundingRect.size.width/2, boundingRect.size.height/2);
transform = CGAffineTransformRotate(transform, angle);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(context, transform);
// Draw the image into the context
CGContextDrawImage(context, CGRectMake(-imageView.image.size.width/2, -imageView.image.size.height/2, imageView.image.size.width, imageView.image.size.height), imageView.image.CGImage);
// Get an image from the context
rotatedImage = [UIImage imageWithCGImage: CGBitmapContextCreateImage(context)];

Get pixels and colours from NSImage

I have created an NSImage object, and ideally would like to determine how many of each pixels colour it contains. Is this possible?
This code renders the NSImage into a CGBitmapContext:
- (void)updateImageData {
if (!_image)
return;
// Dimensions - source image determines context size
NSSize imageSize = _image.size;
NSRect imageRect = NSMakeRect(0, 0, imageSize.width, imageSize.height);
// Create a context to hold the image data
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGContextRef ctx = CGBitmapContextCreate(NULL,
imageSize.width,
imageSize.height,
8,
0,
colorSpace,
kCGImageAlphaPremultipliedLast);
// Wrap graphics context
NSGraphicsContext* gctx = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:NO];
// Make our bitmap context current and render the NSImage into it
[NSGraphicsContext setCurrentContext:gctx];
[_image drawInRect:imageRect];
// Calculate the histogram
[self computeHistogramFromBitmap:ctx];
// Clean up
[NSGraphicsContext setCurrentContext:nil];
CGContextRelease(ctx);
CGColorSpaceRelease(colorSpace);
}
Given a bitmap context, we can access the raw image data directly, and compute the histograms for each colour channel:
- (void)computeHistogramFromBitmap:(CGContextRef)bitmap {
// NB: Assumes RGBA 8bpp
size_t width = CGBitmapContextGetWidth(bitmap);
size_t height = CGBitmapContextGetHeight(bitmap);
uint32_t* pixel = (uint32_t*)CGBitmapContextGetData(bitmap);
for (unsigned y = 0; y < height; y++)
{
for (unsigned x = 0; x < width; x++)
{
uint32_t rgba = *pixel;
// Extract colour components
uint8_t red = (rgba & 0x000000ff) >> 0;
uint8_t green = (rgba & 0x0000ff00) >> 8;
uint8_t blue = (rgba & 0x00ff0000) >> 16;
// Accumulate each colour
_histogram[kRedChannel][red]++;
_histogram[kGreenChannel][green]++;
_histogram[kBlueChannel][blue]++;
// Next pixel!
pixel++;
}
}
}
#end
I've published a complete project, a Cocoa sample app, which includes the above.
https://github.com/gavinb/CocoaImageHistogram.git
I suggest creating your own bitmap context, wrapping it in a graphics context and setting that as the current context, telling the image to draw itself, and then accessing the pixel data behind the bitmap context directly.
This will be more code, but will save you both a trip through a TIFF representation and the creation of thousands or millions of NSColor objects. If you're working with images of any appreciable size, these expenses will add up quickly.
Get an NSBitmapImageRep from your NSImage. Then you can get access to the pixels.
NSImage* img = ...;
NSBitmapImageRep* raw_img = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]];
NSColor* color = [raw_img colorAtX:0 y:0];
Look for "histogram" in the Core Image documentation.
Using colorAtX with NSBitmapImageRep does not always lead to the exact correct color.
I managed to get the correct color with this simple code:
[yourImage lockFocus]; // yourImage is just your NSImage variable
NSColor *pixelColor = NSReadPixel(NSMakePoint(1, 1)); // Or another point
[yourImage unlockFocus];
This maybe a more streamlined approach for some and reduce complexity of dropping into memory management.
https://github.com/koher/EasyImagy
Code sample
https://github.com/koher/EasyImagyCameraSample
import EasyImagy
let image = Image<RGBA<UInt8>>(nsImage: "test.png") // N.B. init with nsImage
print(image[x, y])
image[x, y] = RGBA(red: 255, green: 0, blue: 0, alpha: 127)
image[x, y] = RGBA(0xFF00007F) // red: 255, green: 0, blue: 0, alpha: 127
// Iterates over all pixels
for pixel in image {
// ...
}
//// Gets a pixel by subscripts Gets a pixel by
let pixel = image[x, y]
// Sets a pixel by subscripts
image[x, y] = RGBA(0xFF0000FF)
image[x, y].alpha = 127
// Safe get for a pixel
if let pixel = image.pixelAt(x: x, y: y) {
print(pixel.red)
print(pixel.green)
print(pixel.blue)
print(pixel.alpha)
print(pixel.gray) // (red + green + blue) / 3
print(pixel) // formatted like "#FF0000FF"
} else {
// `pixel` is safe: `nil` is returned when out of bounds
print("Out of bounds")
}

Resources