I have a custom UIImageView class which has the following method:
- (void) cutOutText:(NSString*)stringToStencil;
I want the method to mask the image in a way in which the text is cut out of the image. The result of the method is to have an opaque image with a the transparent text in the centre. So far I have the following:
NSMutableParagraphStyle *textStyle = [NSMutableParagraphStyle new];
[textStyle setAlignment:NSTextAlignmentCenter];
NSAttributedString *initialsString = [[NSAttributedString alloc] initWithString: stringToStencil,attributes:#{NSFontAttributeName:_initialsFont, NSForegroundColorAttributeName:_initialsTextColor, NSParagraphStyleAttributeName: textStyle}];
UIGraphicsBeginImageContextWithOptions(self.image.size, NO, 0.0f);
[self.image drawAtPoint:CGPointMake(0, 0)];
CGFloat fontHeight = _initialsFont.lineHeight;
CGFloat yOffset = (self.frame.size.height - fontHeight) / 2.0;
[initialsString drawInRect:CGRectMake(self.frame.origin.x, yOffset, self.frame.size.width, self.frame.size.height)];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
self.image = result;
However this just prints the text onto the image. How do I create a mask and remove the text from the image instead?
Thanks
D
Related
I need draw string with stroke ,then convert to a UIImage, but in ios 13.7, the "stroke" 's new line not match to "fill" 's new line,
error in ios 13.7:
correct in ios 14 ,16:
after some test ,i find if i don't set NSParagraphStyleAttributeName in attributes, new line is right in ios 13.7 ,it's a bug in ios 13.7?
my code:
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *img = [[self class] drawTextWithStroke];
UIImageView* imageView = [[UIImageView alloc] initWithImage:img];
[self.view addSubview:imageView];
}
+ (UIImage*)drawTextWithStroke
{
NSString* string = #"The operation couldn’t be completed. completed";
CGRect rect = CGRectMake(0, 0, 362, 42);
CGSize size = CGSizeMake(rect.size.width, rect.size.height);
UIFont *font = [UIFont fontWithName:#"Arial-BoldMT" size:18];
// retina display, double resolution
if ([UIScreen instancesRespondToSelector:#selector(scale)] && [[UIScreen mainScreen] scale] == 2.0f) {
UIGraphicsBeginImageContextWithOptions(size, NO, 2.0f);
} else {
UIGraphicsBeginImageContext(size);
}
CGContextRef context = UIGraphicsGetCurrentContext();
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
//
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment: NSTextAlignmentCenter];
attributes[ NSParagraphStyleAttributeName ] = style;
// draw stroke
[attributes setObject:font forKey:NSFontAttributeName];
[attributes setObject:[NSNumber numberWithFloat:3] forKey:NSStrokeWidthAttributeName];
[attributes setObject:[UIColor redColor] forKey:NSStrokeColorAttributeName];
[string drawInRect:rect withAttributes:attributes];
// draw fill
[attributes removeObjectForKey:NSStrokeWidthAttributeName];
[attributes removeObjectForKey:NSStrokeColorAttributeName];
[attributes setObject:[UIColor blackColor] forKey:NSForegroundColorAttributeName];
[string drawInRect:rect withAttributes:attributes];
// convert to image and return
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
The best solution is to draw the text just once specifying both the stroke and the fill attributes together.
But here's the trick - you need to specify a negative stroke width in order to get both the stroke and fill. If you go to the documentation for the NSStrokeWidthAttributeName key you will see the following (emphasis mine):
The value of this attribute is an NSNumber object containing a floating-point value. This value represents the amount to change the stroke width and is specified as a percentage of the font point size. Specify 0 (the default) for no additional changes. Specify positive values to change the stroke width alone. Specify negative values to stroke and fill the text. For example, a typical value for outlined text would be 3.0.
So changing your stroke width to a negative number gives you the desired effect.
Here's an updated version of your code with lots of little cleanup.
+ (UIImage*)drawTextWithStroke {
NSString* string = #"The operation couldn’t be completed. completed";
CGRect rect = CGRectMake(0, 0, 362, 42);
// Added in iOS 4.0 - no need to check for it any more
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
//
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment: NSTextAlignmentCenter];
attributes[ NSParagraphStyleAttributeName ] = style;
UIFont *font = [UIFont fontWithName:#"Arial-BoldMT" size:18];
attributes[NSFontAttributeName] = font;
attributes[NSStrokeWidthAttributeName] = #(-3); // Use negative value for both stroke and fill
attributes[NSStrokeColorAttributeName] = UIColor.redColor;
attributes[NSForegroundColorAttributeName] = UIColor.blackColor;
[string drawInRect:rect withAttributes:attributes];
// convert to image and return
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
My application insert a label and has two sliders rotate and scale the text. After I want to draw this text in a image. See the code of the sliders and method to draw the text.
- (IBAction)angleSliderDidChange:(id)sender {
CGAffineTransform transform = CGAffineTransformMakeRotation(angleSlider.value * M_PI / 180.0);
[myNewLabel setTransform:transform];
}
- (IBAction)scaleSliderDidChange:(id)sender {
[myNewLabel setFont:[UIFont fontWithName:#"HelveticaNeue" size:21*scaleSlider.value]];
[myNewLabel sizeToFit];
[myNewLabel setFont:[UIFont fontWithName:#"HelveticaNeue" size:17*scaleSlider.value]];
}
- (IBAction)editionDidFinish:(id)sender {
UIFont *font = [UIFont fontWithName:#"HelveticaNeue" size:17*scaleSlider.value];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.alignment = NSTextAlignmentRight;
NSDictionary *attributes = #{ NSFontAttributeName:font, NSParagraphStyleAttributeName: paragraphStyle};
UIGraphicsBeginImageContext(bigImageView.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPoint contextCenter = CGPointMake(CGRectGetMidX(myNewLabel.frame), CGRectGetMidY(myNewLabel.frame));
CGContextTranslateCTM(ctx, contextCenter.x, contextCenter.y);
CGContextRotateCTM(ctx, angleSlider.value * M_PI / 180);
CGContextScaleCTM(ctx, scaleSlider.value, scaleSlider.value);
[myNewLabel.text drawInRect:(CGRect){-myNewLabel.frame.size.width/2, -myNewLabel.frame.size.height/2, myNewLabel.frame.size} withAttributes:attributes];
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
bigImageView.image = finalImage;
}
It is a simple procedure but my text is not being drawing in the same place of the added label. I believe I am forgoting some details but I can't see it. And the result is shown on the image below. So, please, help me to fix it.
I need to rotate the String and save this rotating string as UIImage.
Let say my given String is = HELLO WORLD
Now i need to first rotate(say 45 degree) it and than saved as UIImage so i can show this UIImage into my UIImageView
Please help me . So i can rotate the String and save this rotating string as UIImage in Swift?.
I found this answer Drawing rotated text with NSString drawInRect. But not able to achieve the result.
May be the below code helps u,I achieved the same result of rotating the string in 45 degree
-(UIImage*) drawText:(NSString*) text
atPoint:(CGPoint) point
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(65, 65),NO,0.0);
CGRect rect = CGRectMake(point.x, point.y, 50, 30);
[[UIColor whiteColor] set];
CGContextConcatCTM(UIGraphicsGetCurrentContext(), CGAffineTransformMakeTranslation(32.5, 32.5));
CGContextConcatCTM(UIGraphicsGetCurrentContext(), CGAffineTransformMakeRotation(M_PI / 4));
CGContextConcatCTM(UIGraphicsGetCurrentContext(), CGAffineTransformMakeTranslation(-32.5, -32.5));
//[text drawInRect:CGRectIntegral(rect) withFont:font ];
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
[paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
[paragraphStyle setAlignment:NSTextAlignmentCenter];
NSDictionary *attributes = #{ NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue-CondensedBold" size:12],
NSForegroundColorAttributeName: [UIColor blackColor],
NSParagraphStyleAttributeName:paragraphStyle};
[text drawInRect:CGRectIntegral(rect) withAttributes:attributes];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Below is screenshot where i achieved the result
I'm trying to implement draw stroke text with color and border on UIImage but I can't get the solution.
I have used this THLabel for stroke and border of text and it works fine but only when I copy that code into draw image so at that time it's working for me.
Please see the screen shot for the same. Any suggestions?
Draw Text on UIImage Method :
- (UIImage *) drawText:(NSString*)text
inImage:(UIImage*)image
atPoint:(NSString *)location{
CGFloat fontSize = image.size.width*3/30.0;
NSString *fontName = #"Arial";
int w = image.size.width;
int h = image.size.height;
UIFont *font=[UIFont fontWithName:fontName size:fontSize];
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0,0,image.size.width,image.size.height)];
int textFrameY = 25;
int textFrameX = 5;
CGSize aSize = [text sizeWithFont:font];
CGRect rect = CGRectMake(textFrameX,textFrameY, 80, 30);
CGSize lSize = [location sizeWithFont:font];
CGRect rect1 = CGRectMake(image.size.width-lSize.width-fontSize-textFrameX, h-(fontSize*3)-textFrameY, w, h);
[[UIColor blackColor] set];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect1);
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
[[UIColor whiteColor] set];
[text drawInRect:CGRectIntegral(rect) withFont:font];
[location drawInRect:CGRectIntegral(rect1) withFont:font];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;}
THLabel code for stroke color and border for text method:
// Demonstrate stroke.
self.label3.strokeColor = kStrokeColor;
self.label3.strokeSize = kStrokeSize;
I have one UIScrollView with a UIImageView inside from where I obtain a cropped UIImage (considering scale, offset, bounds,...). On the other hand, I have a UILabel which I can rotate, pinch and pan anywhere over the UIScrollView. The problem comes when I try to generate the final image with the label text inside (with it's original/equivalent position, rotation,...) on the cropped UIImage (bigger than screen size).
I've tried everything. This is my last attempt trying to position the text, but it's always misplaced.
// Compute rect to draw the text inside
CGSize imageSize = inImage.size;
NSDictionary *attr = #{NSForegroundColorAttributeName: fontColor, NSFontAttributeName: textFont};
CGSize textSize = [text sizeWithAttributes:attr];
CGRect textRect = CGRectMake(labelCenterPoint.x, labelCenterPoint.y, textSize.width, textSize.height);
// Create the image
UIGraphicsBeginImageContext(imageSize);
[inImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
// Rotate text from center
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM( context, 0.5f * textSize.width, 0.5f * textSize.height ) ;
CGContextRotateCTM(context, rotation);
[text drawInRect:CGRectIntegral(textRect) withAttributes:attr];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I think the problem is the textRect origin. Do I have to make the UILabel a subView of the UIScrollView too and make the textRect origin relative to the UIScrollView parameters? or am I missing something?. Is there a better way?.
Thanks in advance!.
Edit:
I have finally found an acceptable solution (although is not too much precise). I think the problem was about the two existing coordinate systems, so I tried a different approach:
// Calculate the relative position of the UILabel with respect
// to the ScrollView that contains the image
float xPosition = label.frame.origin.x - scrollView.frame.origin.x;
float yPosition = label.frame.origin.y - scrollView.frame.origin.y;
// Calculate the proportion to apply to the position
// (the image is bigger than screen)
float xProportion = image.frame.size.width / scrollView.frame.size.width;
float yProportion = image.frame.size.height / scrollView.frame.size.height;
UIImageView *testView = [[UIImageView alloc] initWithImage:image];
UILabel *tempLabel = [[UILabel alloc] initWithFrame:
CGRectMake((xPosition*xProportion),
(yPosition*yProportion),
label.frame.size.width*xProportion,
label.frame.size.height*yProportion)];
tempLabel.text = label.text;
tempLabel.font = [UIFont fontWithName:label.font.fontName
size:label.font.pointSize*3];
tempLabel.textColor = label.textColor;
tempLabel.textAlignment = NSTextAlignmentCenter;
tempLabel.transform = CGAffineTransformMakeRotation(rotation);
tempLabel.adjustsFontSizeToFitWidth = YES;
tempLabel.minimumScaleFactor = 0.05;
[testView addSubview:tempLabel];
UIGraphicsBeginImageContext(testView.image.size);
[testView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *imageWithText = UIGraphicsGetImageFromCurrentImageContext();
I hope this may help someone. All suggestions are welcome of course.