Is there a way to create rounded NSLabel in MacOS? - swift

Currently, in my SpriteKit app, I'm adding a new NSLabel node into another node which has the shape of arc, like:
let labelNode = SKLabelNode(text: text)
labelNode.color = .black
labelNode.horizontalAlignmentMode = .center
labelNode.verticalAlignmentMode = .center
labelNode.position = CGPoint(x: node.frame.midX, y: node.frame.midY)
labelNode.fontColor = .red
addChild(labelNode)
but after adding it looks like this
So is there a way, where I can make those NSLabels inside of arcs "rounded", in the shape of arcs in which they are locating?
After a few hours of searching, I really could not find anything helpful, so I hope you can show me the path
P.S I'm asking for MacOS, not for iOS.

Apple published Objective-C sample code in 2013:
Drawing Along a Path Using Core Text with Cocoa
CoreTextArcCocoa demonstrates how you can use Core Text to draw text along an arc in a Cocoa application. It also illustrates how you can use the Cocoa font panel to receive font settings that can be used by Core Text to select the font used for drawing.
You might be able to adapt that to your needs. Remember that you can call Objective-C code from Swift by including the header files in your bridging header.
However, if your label text doesn't change at runtime (or only has a few possible values), it's probably easier to just use an image editor to create images of curved text.
In case the link to the sample project breaks, here's the relevant part of the header file “APLCoreTextArcView.h”:
#import <Cocoa/Cocoa.h>
#interface APLCoreTextArcView : NSView
#property (nonatomic) NSFont *font;
#property (nonatomic) NSString *string;
#property (readonly, nonatomic) NSAttributedString *attributedString;
#property (nonatomic) CGFloat radius;
#property (nonatomic) BOOL showsGlyphBounds;
#property (nonatomic) BOOL showsLineMetrics;
#property (nonatomic) BOOL dimsSubstitutedGlyphs;
#end
And here's the relevant part of the implementation file “APLCoreTextArcView.m”:
#import "APLCoreTextArcView.h"
#import <AssertMacros.h>
#define ARCVIEW_DEFAULT_FONT_NAME #"Didot"
#define ARCVIEW_DEFAULT_FONT_SIZE 64.0
#define ARCVIEW_DEFAULT_RADIUS 150.0
typedef struct GlyphArcInfo {
CGFloat width;
CGFloat angle; // in radians
} GlyphArcInfo;
static void PrepareGlyphArcInfo(CTLineRef line, CFIndex glyphCount, GlyphArcInfo *glyphArcInfo)
{
NSArray *runArray = (__bridge NSArray *)CTLineGetGlyphRuns(line);
// Examine each run in the line, updating glyphOffset to track how far along the run is in terms of glyphCount.
CFIndex glyphOffset = 0;
for (id run in runArray) {
CFIndex runGlyphCount = CTRunGetGlyphCount((__bridge CTRunRef)run);
// Ask for the width of each glyph in turn.
CFIndex runGlyphIndex = 0;
for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) {
glyphArcInfo[runGlyphIndex + glyphOffset].width = CTRunGetTypographicBounds((__bridge CTRunRef)run, CFRangeMake(runGlyphIndex, 1), NULL, NULL, NULL);
}
glyphOffset += runGlyphCount;
}
double lineLength = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CGFloat prevHalfWidth = glyphArcInfo[0].width / 2.0;
glyphArcInfo[0].angle = (prevHalfWidth / lineLength) * M_PI;
// Divide the arc into slices such that each one covers the distance from one glyph's center to the next.
CFIndex lineGlyphIndex = 1;
for (; lineGlyphIndex < glyphCount; lineGlyphIndex++) {
CGFloat halfWidth = glyphArcInfo[lineGlyphIndex].width / 2.0;
CGFloat prevCenterToCenter = prevHalfWidth + halfWidth;
glyphArcInfo[lineGlyphIndex].angle = (prevCenterToCenter / lineLength) * M_PI;
prevHalfWidth = halfWidth;
}
}
#implementation APLCoreTextArcView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
_font = [NSFont fontWithName:ARCVIEW_DEFAULT_FONT_NAME size:ARCVIEW_DEFAULT_FONT_SIZE];
_string = #"Curvaceous Type";
_radius = ARCVIEW_DEFAULT_RADIUS;
_showsGlyphBounds = NO;
_showsLineMetrics = NO;
_dimsSubstitutedGlyphs = NO;
}
return self;
}
- (void)drawRect:(NSRect)rect {
// Don't draw if we don't have a font or string
if (self.font == NULL || self.string == NULL)
return;
// Initialize the text matrix to a known value
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// Draw a white background
[[NSColor whiteColor] set];
NSRectFill(rect);
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)self.attributedString);
assert(line != NULL);
CFIndex glyphCount = CTLineGetGlyphCount(line);
if (glyphCount == 0) {
CFRelease(line);
return;
}
GlyphArcInfo * glyphArcInfo = (GlyphArcInfo*)calloc(glyphCount, sizeof(GlyphArcInfo));
PrepareGlyphArcInfo(line, glyphCount, glyphArcInfo);
// Move the origin from the lower left of the view nearer to its center.
CGContextSaveGState(context);
CGContextTranslateCTM(context, CGRectGetMidX(NSRectToCGRect(rect)), CGRectGetMidY(NSRectToCGRect(rect)) - self.radius / 2.0);
// Stroke the arc in red for verification.
CGContextBeginPath(context);
CGContextAddArc(context, 0.0, 0.0, self.radius, M_PI, 0.0, 1);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextStrokePath(context);
// Rotate the context 90 degrees counterclockwise.
CGContextRotateCTM(context, M_PI_2);
/*
Now for the actual drawing. The angle offset for each glyph relative to the previous glyph has already been calculated; with that information in hand, draw those glyphs overstruck and centered over one another, making sure to rotate the context after each glyph so the glyphs are spread along a semicircular path.
*/
CGPoint textPosition = CGPointMake(0.0, self.radius);
CGContextSetTextPosition(context, textPosition.x, textPosition.y);
CFArrayRef runArray = CTLineGetGlyphRuns(line);
CFIndex runCount = CFArrayGetCount(runArray);
CFIndex glyphOffset = 0;
CFIndex runIndex = 0;
for (; runIndex < runCount; runIndex++) {
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CFIndex runGlyphCount = CTRunGetGlyphCount(run);
Boolean drawSubstitutedGlyphsManually = false;
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
/*
Determine if we need to draw substituted glyphs manually. Do so if the runFont is not the same as the overall font.
*/
if (self.dimsSubstitutedGlyphs && ![self.font isEqual:(__bridge NSFont *)runFont]) {
drawSubstitutedGlyphsManually = true;
}
CFIndex runGlyphIndex = 0;
for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) {
CFRange glyphRange = CFRangeMake(runGlyphIndex, 1);
CGContextRotateCTM(context, -(glyphArcInfo[runGlyphIndex + glyphOffset].angle));
// Center this glyph by moving left by half its width.
CGFloat glyphWidth = glyphArcInfo[runGlyphIndex + glyphOffset].width;
CGFloat halfGlyphWidth = glyphWidth / 2.0;
CGPoint positionForThisGlyph = CGPointMake(textPosition.x - halfGlyphWidth, textPosition.y);
// Glyphs are positioned relative to the text position for the line, so offset text position leftwards by this glyph's width in preparation for the next glyph.
textPosition.x -= glyphWidth;
CGAffineTransform textMatrix = CTRunGetTextMatrix(run);
textMatrix.tx = positionForThisGlyph.x;
textMatrix.ty = positionForThisGlyph.y;
CGContextSetTextMatrix(context, textMatrix);
if (!drawSubstitutedGlyphsManually) {
CTRunDraw(run, context, glyphRange);
}
else {
/*
We need to draw the glyphs manually in this case because we are effectively applying a graphics operation by setting the context fill color. Normally we would use kCTForegroundColorAttributeName, but this does not apply as we don't know the ranges for the colors in advance, and we wanted demonstrate how to manually draw.
*/
CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, glyphRange, &glyph);
CTRunGetPositions(run, glyphRange, &position);
CGContextSetFont(context, cgFont);
CGContextSetFontSize(context, CTFontGetSize(runFont));
CGContextSetRGBFillColor(context, 0.25, 0.25, 0.25, 0.5);
CGContextShowGlyphsAtPositions(context, &glyph, &position, 1);
CFRelease(cgFont);
}
// Draw the glyph bounds
if ((self.showsGlyphBounds) != 0) {
CGRect glyphBounds = CTRunGetImageBounds(run, context, glyphRange);
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextStrokeRect(context, glyphBounds);
}
// Draw the bounding boxes defined by the line metrics
if ((self.showsLineMetrics) != 0) {
CGRect lineMetrics;
CGFloat ascent, descent;
CTRunGetTypographicBounds(run, glyphRange, &ascent, &descent, NULL);
// The glyph is centered around the y-axis
lineMetrics.origin.x = -halfGlyphWidth;
lineMetrics.origin.y = positionForThisGlyph.y - descent;
lineMetrics.size.width = glyphWidth;
lineMetrics.size.height = ascent + descent;
CGContextSetRGBStrokeColor(context, 0.0, 1.0, 0.0, 1.0);
CGContextStrokeRect(context, lineMetrics);
}
}
glyphOffset += runGlyphCount;
}
CGContextRestoreGState(context);
free(glyphArcInfo);
CFRelease(line);
}
- (NSAttributedString *)attributedString {
// Create an attributed string with the current font and string.
assert(self.font != nil);
assert(self.string != nil);
// Create our attributes.
NSDictionary *attributes = #{NSFontAttributeName: self.font, NSLigatureAttributeName: #0};
assert(attributes != nil);
// Create the attributed string.
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:self.string attributes:attributes];
return attrString;
}
#end

Related

How to draw text in NPAPI plugin CGContextRef?

I want to draw text in the NPAPI plugin CGContextRef, but i don’t know to make it work.
I get the CGContext Ref as follow:
int16_t NPP_HandleEvent(NPP instance, void* event)
{
int16_t iRet = 0;
PluginObject *obj = (PluginObject *)instance->pdata;
NPCocoaEvent *cocoaEvent = (NPCocoaEvent *)event;
switch(cocoaEvent->type)
{
case NPCocoaEventDrawRect:
obj->m_NPContext = CGContextRetain(cocoaEvent->data.draw.context);
DrawSealOnContext(obj->m_NPContext, obj->m_pstSealAPInfo);
iRet = 1;
break;
default:
iRet = 0;
break;
}
return iRet;
}
In function DrawSealOnContext, I want to draw an Ellipse and some text in the window. the function as follow:
int DrawSealOnContext(CGContextRef contextRef, PSEAL_APPEARANCE_INFO pstSealAPInfo)
{
// draw ellipses
CGRect rect = {2, 25, 146, 100};
CGContextSetLineWidth(contextRef, 4.0);
CGContextSetStrokeColorWithColor(contextRef, [NSColor blueColor].CGColor);
CGContextBeginPath(contextRef);
CGContextAddEllipseInRect(contextRef, rect);
CGContextDrawPath(contextRef, kCGPathStroke);
// draw text
CGContextSetFillColorWithColor(contextRef, [NSColor blueColor].CGColor);
NSMutableParagraphStyle * paragraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
[paragraphStyle setAlignment:NSCenterTextAlignment];
NSDictionary * attributes = [NSDictionary dictionaryWithObject:paragraphStyle
forKey:NSParagraphStyleAttributeName];
NSString * mystr = #“hello\n and";
NSRect strFrame = { { 0, 0 }, { 150, 150 } };
[mystr drawInRect:strFrame withAttributes:attributes];
}
I can get the ellipse on the screen, but the text doesn’t show up?
I also try this:
// draw text
CGContextShowText(contextRef, "hello", 5);
It doesn’t work either.
what’s wrong with my program, really appreciate your
answers.
I have solve this problem, with the help of this article:
http://www.cocoabuilder.com/archive/cocoa/240527-setting-cgcontextref-to-the-current-context.html
The problem is that, function
[mystr drawInRect:strFrame withAttributes:attributes];
draw test in current context, but my current is not the the context "CGContextRef contextRef" i decleared. So, before i used drawInRect method, i need to set contextRef my current context.
How to set CGContextRef to current context, please refer to the website i pasted.
Hope this answer can solve your problem!

Draw lines with centers without them overlap

I have a problem when i try to join lines. Here is a picture:
and I like that it looks like this:
and my code is:
- (void) drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
const CGFloat *components = CGColorGetComponents(self.lineColor.CGColor);
CGFloat red;
CGFloat green;
CGFloat blue;
CGFloat alpha;
if(CGColorGetNumberOfComponents(self.lineColor.CGColor) == 2)
{
red = 1;
green = 1;
blue = 1;
alpha = 1;
}
else
{
red = components[0];
green = components[1];
blue = components[2];
alpha = components[3];
if (alpha <= 0) alpha = 1;
}
// set the stroke color and width
CGContextSetRGBStrokeColor(context, red, green, blue, alpha);
CGContextSetLineWidth(context, 2.0);
if (self.points.count >0) {
BezierPoint *firstPoint = [self.points objectAtIndex:0];
CGContextMoveToPoint(context, firstPoint.center.x, firstPoint.center.y);
int index = 0;
for (BezierPoint *point in self.points ) {
if(index == 0){
index++;
continue;
}
CGContextAddLineToPoint(context, point.center.x, point.center.y);
}
CGContextAddLineToPoint(context, firstPoint.center.x, firstPoint.center.y);
}
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 0.0);
CGContextDrawPath(context, kCGPathFillStroke);
}
the problem I have is that for every point you add, the lines overlap and I would like that as I stay I add points for geometric figure that globulins do not overlap
If anyone can help me I will thank!!
I would move the logic that determines the order the lines are drawn to your view controller, and access the data needed to draw the view with from a delegate using a protocol. Views should not own their data. For example, in your .h try something like;
#import <UIKit/UIKit.h>
#protocol CowDrawViewDataSource
-(NSArray *) pointsToDraw;
#end
#interface CowDrawView : UIView
#property (nonatomic , weak) id <CowDrawViewDataSource> dataSource;
#end
Then, (and I hope this does answer you question) in the view controller, you set as your views delegate, construct the
-(NSArray *) pointsToDraw;
method, in such a way, to send your array of points in an order in from which they can be drawn. Say by finding the point the top/left point, then the top/right, then bottom/right, bottom/left. (although such an approach might not work so well with irregular polygons, and shapes that are contiguous, but not polygons.)
After you figure out how to get the array in the order you want, you can get it in your drawRect by sending a message to it's delegate such as
NSArray *points = [self.dataSource pointToDraw];
with little re-work in the drawRect it's self.
The easy way to correct your problem is first draw only single line segments between two points and see the results. Then you can form loops. I guess you are not assigning perfect coordinates points or your loop must consist of :
CGContextAddLineToPoint(context, point.center.x, point.center.y);
And then,
CGContextMoveToPoint(context, point.center.x, point.center.y);
This shows that first draw line and move to next point.
Hope this helps.

Identify a percentage of transparent pixels in an area of UIImageView

I'm trying to set up a collision type hit test for a defined of pixels within a UIImageView. I'm only wish to cycle through pixels in a defined area.
Here's what I have so far:
- (BOOL)cgHitTestForArea:(CGRect)area {
BOOL hit = FALSE;
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
float areaFloat = ((area.size.width * 4) * area.size.height);
unsigned char *bitmapData = malloc(areaFloat);
CGContextRef context = CGBitmapContextCreate(bitmapData,
area.size.width,
area.size.height,
8,
4*area.size.width,
colorspace,
kCGImageAlphaPremultipliedLast);
CGContextTranslateCTM(context, -area.origin.x, -area.origin.y);
[self.layer renderInContext:context];
//Seek through all pixels.
float transparentPixels = 0;
for (int i = 0; i < (int)areaFloat ; i += 4) {
//Count each transparent pixel.
if (((bitmapData[i + 3] * 1.0) / 255.0) == 0) {
transparentPixels += 1;
}
}
free(bitmapData);
//Calculate the percentage of transparent pixels.
float hitTolerance = [[self.layer valueForKey:#"hitTolerance"]floatValue];
NSLog(#"Apixels: %f hitPercent: %f",transparentPixels,(transparentPixels/areaFloat));
if ((transparentPixels/(areaFloat/4)) < hitTolerance) {
hit = TRUE;
}
CGColorSpaceRelease(colorspace);
CGContextRelease(context);
return hit;
}
Is someone able to offer any reason why it isn't working?
I would suggest using ANImageBitmapRep. It allows for easy pixel-level manipulation of images without the hassle of context, linking against other libraries, or raw memory allocation. To create an ANImgaeBitmapRep with the contents of a view, you could do something like this:
BMPoint sizePt = BMPointMake((int)self.frame.size.width,
(int)self.frame.size.height);
ANImageBitmapRep * irep = [[ANImageBitmapRep alloc] initWithSize:sizePt];
CGContextRef ctx = [irep context];
[self.layer renderInContext:context];
[irep setNeedsUpdate:YES];
Then, you can crop out your desired rectangle. Note that coordinates are relative to the bottom left corner of the view:
// assuming aFrame is our frame
CGRect cFrame = CGRectMake(aFrame.origin.x,
self.frame.size.height - (aFrame.origin.y + aFrame.size.height),
aFrame.size.width, aFrame.size.height);
[irep cropFrame:];
Finally, you can find the percentage of alpha in the image using the following:
double totalAlpha;
double totalPixels;
for (int x = 0; x < [irep bitmapSize].x; x++) {
for (int y = 0; y < [irep bitmapSize].y; y++) {
totalAlpha += [irep getPixelAtPoint:BMPointMake(x, y)].alpha;
totalPixels += 1;
}
}
double alphaPct = totalAlpha / totalPixels;
You can then use the alphaPct variable as a percentage from 0 to 1. Note that, to prevent leaks, you must release the ANImageBitmapRep object using release: [irep release].
Hope that I helped. Image data is a fun and interesting field when it comes to iOS development.

Drawing gradients on a UIView doesn't work on subsequent calls to drawRect:

I'm trying to debug an issue that is really stumping me. Basically, I have a UIView subclass that draws a gradient to itself in drawRect: based on colors that i provide in the init method. The problem occurs when the view draws itself again. If i call setNeedsDisplay on the view, or if say the view is removed due to an out of memory warning and it gets re-added later (which would also trigger drawRect: again) then the background of the view is completely blank. I'm not sure why redrawing a gradient would cause this to happen; does anybody know what could be going wrong? Here's an excerpt of the code I have in drawRect:
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
int startcolorNumComponents = CGColorGetNumberOfComponents(self.topColor.CGColor);
int endcolorNumComponents = CGColorGetNumberOfComponents(self.bottomColor.CGColor);
// get the array of floats for each of the gradient colors
const CGFloat *startComponents = CGColorGetComponents(self.topColor.CGColor);
const CGFloat *endComponents = CGColorGetComponents(self.bottomColor.CGColor);
CGFloat redStart = startComponents[0];
CGFloat greenStart = (startcolorNumComponents != 2 )? startComponents[1] : startComponents[0];
CGFloat blueStart = (startcolorNumComponents != 2 )? startComponents[2] : startComponents[0];
CGFloat startAlpha = (startcolorNumComponents != 2 )? startComponents[3] : startComponents[1];
CGFloat redEnd = endComponents[0];
CGFloat greenEnd = (endcolorNumComponents != 2) ? endComponents[1] : endComponents[0];
CGFloat blueEnd = (endcolorNumComponents != 2) ? endComponents[2] : endComponents[0];
CGFloat endAlpha = (endcolorNumComponents != 2) ? endComponents[3] : endComponents[1];
CGFloat components[8] = { redStart, greenStart, blueStart, startAlpha, redEnd, greenEnd, blueEnd, endAlpha };
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGGradientRef glossGradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), components, locations, num_locations);
CGPoint gradientStart = CGPointMake(CGRectGetMidX(self.bounds), self.bounds.origin.x);
CGPoint gradientEnd = CGPointMake(CGRectGetMidX(self.bounds),self.bounds.size.height));
CGContextDrawLinearGradient(currentContext, glossGradient, gradientStart, gradientEnd, 0.0);
CGGradientRelease(glossGradient);
CGContextRestoreGState(currentContext);
I think this turned out to be a memory problem. After fixing memory leaks this no longer happens for me.

Pixel-Position of Cursor in UITextView

Is there a way of getting the position (CGPoint) of the cursor (blinking bar) in an UITextView (preferable relative to its content). I don’t mean the location as an NSRange. I need something around:
- (CGPoint)cursorPosition;
It should be a non-private API way.
Requires iOS 5
CGPoint cursorPosition = [textview caretRectForPosition:textview.selectedTextRange.start].origin;
Remember to check that selectedTextRange is not nil before calling this method. You should also use selectedTextRange.empty to check that it is the cursor position and not the beginning of a text range. So:
if (textview.selectedTextRange.empty) {
// get cursor position and do stuff ...
}
SWIFT 4 version:
if let cursorPosition = textView.selectedTextRange?.start {
// cursorPosition is a UITextPosition object describing position in the text (text-wise description)
let caretPositionRectangle: CGRect = textView.caretRect(for: cursorPosition)
// now use either the whole rectangle, or its origin (caretPositionRectangle.origin)
}
textView.selectedTextRange?.start returns a text position of the cursor, and we then simply use textView.caretRect(for:) to get its pixel position in textView.
It's painful, but you can use the UIStringDrawing additions to NSString to do it. Here's the general algorithm I used:
CGPoint origin = textView.frame.origin;
NSString* head = [textView.text substringToIndex:textView.selectedRange.location];
CGSize initialSize = [head sizeWithFont:textView.font constrainedToSize:textView.contentSize];
NSUInteger startOfLine = [head length];
while (startOfLine > 0) {
/*
* 1. Adjust startOfLine to the beginning of the first word before startOfLine
* 2. Check if drawing the substring of head up to startOfLine causes a reduction in height compared to initialSize.
* 3. If so, then you've identified the start of the line containing the cursor, otherwise keep going.
*/
}
NSString* tail = [head substringFromIndex:startOfLine];
CGSize lineSize = [tail sizeWithFont:textView.font forWidth:textView.contentSize.width lineBreakMode:UILineBreakModeWordWrap];
CGPoint cursor = origin;
cursor.x += lineSize.width;
cursor.y += initialSize.height - lineSize.height;
return cursor;
}
I used [NSCharacterSet whitespaceAndNewlineCharacterSet] to find word boundaries.
This can also be done (presumably more efficiently) using CTFrameSetter in CoreText, but that is not available in iPhone OS 3.1.3, so if you're targeting the iPhone you will need to stick to UIStringDrawing.
Yes — as in there's a method to get the cursor position. Just use
CGRect caretRect = [textView rectContainingCaretSelection];
return caretRect.origin;
No — as in this method is private. There's no public API for this.
I try to mark a selected text, i.e. I receive a NSRange and want to draw a yellow rectangle behind that text. Is there another way?
I can advise you some trick:
NSRange selectedRange = myTextView.selectedRange;
[myTextView select:self];
UIMenuController* sharedMenu = [UIMenuController sharedMenuController];
CGRect menuFrame = [sharedMenu menuFrame];
[sharedMenu setMenuVisible:NO];
myTextView.selectedRange = selectedRange
Using this code, you can know get the position of the cut/copy/past menu and there place your yellow rectangle.
I did not find a way to get the menu position witout forcing it to appear by a simulated select operation.
Regards
Assayag
Take a screenshot of the UITextView, then search the pixel data for colors that match the color of the cursor.
-(CGPoint)positionOfCursorForTextView:(UITextView)textView {
//get CGImage from textView
UIGraphicsBeginImageContext(textView.bounds.size);
[textView.layer renderInContext:UIGraphicsGetCurrentContext()];
CGImageRef textImageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage;
UIGraphicsEndImageContext();
//get raw pixel data
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
uint8_t * textBuffer = (uint8_t*)malloc(Width * Height * 4);
NSUInteger bytesPerRow = 4 * Width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(textBuffer, Width, Height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, Width, Height), textImageRef);
CGContextRelease(context);
//search
for(int y = 0; y < Height; y++)
{
for(int x = 0; x < Width * 4; x += 4)
{
int red = textBuffer[y * 4 * (NSInteger)Width + x];
int green = textBuffer[y * 4 * (NSInteger)Width + x + 1];
int blue = textBuffer[y * 4 * (NSInteger)Width + x + 2];
int alpha = textBuffer[y * 4 * (NSInteger)Width + x + 3];
if(COLOR IS CLOSE TO COLOR OF CURSOR)
{
free(textBuffer);
CGImageRelease(textImageRef);
return CGPointMake(x/4, y);
}
}
}
free(textBuffer);
CGImageRelease(textImageRef);
return CGPointZero;
}