Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Placing button as subview just next to the UILable/UITextView's last character

I have used four UITextviews to place each paragraphs in. Now I want to add a UIButton as subview for each UITextview.

I realized creating subview with frame CGRectMake(textview.frame.width-50,textview.frame.height-20,50,20) is not a ideal solution because the sentence may end anywhere, I mean the last character of any paragraph may end anywhere, its position is not constant though.

So the bottom line is, I want to add UIButton within the UITextView,just next to lats word. How do I do that?

Any solution will be greatly appreciated. :)

like image 731
Master Stroke Avatar asked Oct 21 '22 19:10

Master Stroke


1 Answers

It's not so simple. Using:

CGSize sizeOfString = [string sizeWithFont:self.aTextView.font constrainedToSize:self.aTextView.frame.size];

you can obtain a CGSize for the given string. But, giving the entire string, will result in a size equal to your UITextView size (so, the bottom-right corner)

You have to use this method on the last line of each UITextField...and this is the difficult part. The word wrap is done by CoreText, UITextView or UILabel doesn't give you information about the single lines, positions, etc.

You have to calculate it by yourself doing something similar (the code is not so clean, I'll help later if you have problem understanding):

NSAttributedString* text = self.aTextView.attributedText;
CTFramesetterRef fs =
CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)text);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,self.aTextView.frame.size.width - 16,100000));  // why the -16? the frame should be the REAL TEXT FRAME, not the UITextView frame. If you look, there is a space between the view margin and the text. This is the baseline. Probably there is a method to calculate it programatically, but I can't check now. In my case it seems like 8px (*2)
CTFrameRef f = CTFramesetterCreateFrame(fs, CFRangeMake(0, 0), path, NULL);
CTFrameDraw(f, NULL);

NSRange lastRange;
NSArray* lines = (__bridge NSArray*)CTFrameGetLines(f);

id lastLineInArray = [lines lastObject];
CTLineRef theLine = (__bridge CTLineRef)lastLineInArray;
CFRange range = CTLineGetStringRange(theLine);
lastRange.length = range.length;
lastRange.location = range.location - 1;
NSLog(@"%ld %ld", range.location, range.length);

CGPathRelease(path);
CFRelease(f);
CFRelease(fs);

// this is the last line
NSString *lastLine = [self.aTextView.text substringWithRange:lastRange];

Now, you can use:

CGSize sizeOfString = [lastLine sizeWithFont:self.aTextView.font constrainedToSize:self.aTextView.frame.size];

you will obtain the width and the height of the string, and then the final position of your button (for the y position: number of lines taken from the lines array count * string height)

EDIT: a comment about the left space in the UITextView (the reason why there is a -16 in this line)

CGPathAddRect(path, NULL, CGRectMake(0,0,self.aTextView.frame.size.width - 16,100000));

It happens because the text is not really inside the UITextView, but inside one of its subviews: an UIWebDocumentView (private class) which adds an insets. After some searching, I can't find any method to obtain (legally) the value of this insets, necessary to pass the correct rect to the CGPathAddRect() function. You can do some tests to be sure that it's always 8pt :-) or switch to UILabel, which doesn't have that content insets

like image 198
LombaX Avatar answered Oct 27 '22 19:10

LombaX