How to get n lines of text from UITextView in iOS - ios

In my application i need to restrict to user enter 7 lines only in UITextView. When he tries enter text in 8 line we need to stop allowing editing and show an alert message. Its working fine by using CGSize of getting UITextView text.
But when user enters text in UITextView as Paste its not allowing if text is more than 7 lines. As per requirement i need to get the 7 lines of Text from entred (Copied & Pasted ) in to UITextView.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string
{
NSString *temp = [textView.text stringByReplacingCharactersInRange:range withString:string]
CGSize size = [temp sizeWithFont:textView.font constrainedToSize:CGSizeMake(textView.frame.size.width,999) lineBreakMode:UILineBreakModeWordWrap];
int numLines = size.height / textView.font.lineHeight;
if (numLines <= 8)
{
return true;
}
//Alert
return false;
}
Please help me in this issue.
Thanks in Advance.

You could use the UITextViewTextDidChangeNotification notification to alert you on other changes like text being pasted in. (referenced at the bottom of this page)
Or, there is also this handy category on UITextView that allows it to work with the UITextField UIControl events that can be found here.

Related

Building a facebook messenger style new message friend addition - uitextfield / uitextview

I'm trying to make a fb messenger style new message formation uitextview.. as shown in the image..
Specifically, how to make the uitextview which is at the top of the tableview (showing list of selected friends). I want to make a uitextview with the following properties..
1) It expands/contracts as more names are added/removed to/from it.
2) The textview is editable - but not partially editable, i.e., a name is either wiped out by backspace or not (like how it happens in fb)
3) Possibly this editing happens in nice aesthetics (similar to fb, make the color of the entire text blue colored before backspacing it out)
It has been a while since I have used objective-c, but I believe this is correct. It may not be though. And yes I would eventually switch to swift.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
//you should have an array of names as a property, not here.
NSMutableArray* nameArray = [#"sam"];
if ([text isEqual: #""]) {// this is delete
for (NSString *name in nameArray) {
NSRange nameRange = [textView.text rangeOfString:name];
if (nameRange.location == range.location) {
textView.text = [textView.text stringByReplacingCharactersInRange:nameRange withString:#""];
[nameArray removeObject:name]
return false;
}
}
}
return true;
}

hyperlink to section of text in uitextview

I am trying to set up a 'Frequently Asked Questions' section on a UITextView. How do I link a line of text on a UITextView so that when the user clicks on it, the UITextView scrolls to a section of text in the same view. I would also like to underline the text and change the text color to blue.
try TTTAttributedLabel
TTTAttributedLabel allows you to automatically detect links for dates, addresses, links, phone numbers, transit information, or allow you to embed your own.
label.enabledTextCheckingTypes = NSTextCheckingTypeLink; // Automatically detect links when the label text is subsequently changed
label.delegate = self; // Delegate methods are called when the user taps on a link (see `TTTAttributedLabelDelegate` protocol)
label.text = #"Fork me on GitHub! (http://github.com/mattt/TTTAttributedLabel/)"; // Repository URL will be automatically detected and linked
NSRange range = [label.text rangeOfString:#"me"];
[label addLinkToURL:[NSURL URLWithString:#"http://github.com/mattt/"] withRange:range]; // Embedding a custom link in a substring
You need to first fetch the click event of the text "Frequentry asked questions". on the click event you need to make code for scrolling.
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
//Set your character range here
// if match return TRUE .
// else return FALSE.
}
On successful character range fetch scroll your textView to the questions by using this method.
CGPoint bottomOffset;
bottomOffset = CGPointMake(0,(Y value of the question));
[self.chatOutput setContentOffset:bottomOffset animated:YES];
This method will scroll the uitextview to the position of your question.
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:#"Google"];
[str addAttribute: NSLinkAttributeName value: #"http://www.google.com" range: NSMakeRange(0, str.length)];
yourTextField.attributedText = str;

Limit the number of line in UITextView

Question
Is there any way to "accurately" limit the number of line in UITextView for target iOS 5.0?
What I had tried
As I had search in stack overflow. I had found these question been ask before in links below.
In UITextView, how to get the point that next input will begin another line
Limit the number of lines for UITextview
Limit number of lines in UITextView
But I still can't get the accurate number of line in UITextView when I tried to decide whether to return YES or NO in textView:shouldChangeTextInRange:replacementText:.
I had tried used the code which is the answer of Limiting text in a UITextView and the code after modified (remove -15 in the answer) is showing below.
- (BOOL)textView:(UITextView *)aTextView shouldChangeTextInRange:(NSRange)aRange replacementText:(NSString*)aText
{
NSString* newText = [aTextView.text stringByReplacingCharactersInRange:aRange withString:aText];
// TODO - find out why the size of the string is smaller than the actual width, so that you get extra, wrapped characters unless you take something off
CGSize tallerSize = CGSizeMake(aTextView.frame.size.width,aTextView.frame.size.height*2); // pretend there's more vertical space to get that extra line to check on
CGSize newSize = [newText sizeWithFont:aTextView.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
if (newSize.height > aTextView.frame.size.height)
{
[myAppDelegate beep];
return NO;
}
else
return YES;
}
I also figure out a way to get the number of line in UITextView. The way is to calculate by contentSize property like textView.contenSize.height/font.lineHeight. This method can get the accurate number of lines in UITextView. But the problem is that contentSize get in textView:shouldChangeTextInRange:replacementText: and textViewDidChange: is the old contentSize. So I still can't limit the number of lines in UITextView.
Solution I used
This is kind of workaround but at least it work.
Step 1
At first you need to create a temporary new UITextView with all the same as the original UITextView but setting the temporary UITextView hidden in .xib file. In this sample code I name the temporary UITextView as tempTextInputView
Step 2
Add new referencing outlet to .h file like
#property (retain, nonatomic) IBOutlet UITextView *tempTextInputView;// Use to calculate the number of lines in UITextView with new text
Step 3
Add code below.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text];
_tempTextInputView.text = newText;
// Calcualte the number of lines with new text in temporary UITextView
CGRect endRectWithNewText = [_tempTextInputView caretRectForPosition:_tempTextInputView.endOfDocument];
CGRect beginRectWithNewText = [_tempTextInputView caretRectForPosition:_tempTextInputView.beginningOfDocument];
float beginOriginY = beginRectWithNewText.origin.y;
float endOriginY = endRectWithNewText.origin.y;
int numberOfLines = (endOriginY - beginOriginY)/textView.font.lineHeight + 1;
if (numberOfLines > maxLinesInTextView) {// Too many lines
return NO;
}else{// Number of lines will not over the limit
return YES;
}
}
Discussion
maxLinesInTextView is an int variable represent the maximum number of lines you want.
I use a temporary UITextView to setting new text is because when I setting the new text simply in the original UITextView, I got some problem when I typing in ChuYin(注音) keyboard which is a Traditional Chinese input method.
I still using textView:shouldChangeTextInRange:replacementText: but not textViewDidChange: is because I got some problem when cache the text before modify with a global NSString and replace the UITextView.text with that global NSString in textViewDidChange:.
Here's how you can use the UITextViewDelegate shouldChangeTextInRange: method to limit the text entry to the height of the text view:
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// Combine the new text with the old
let combinedText = (textView.text as NSString).stringByReplacingCharactersInRange(range, withString: text)
// Create attributed version of the text
let attributedText = NSMutableAttributedString(string: combinedText)
attributedText.addAttribute(NSFontAttributeName, value: textView.font, range: NSMakeRange(0, attributedText.length))
// Get the padding of the text container
let padding = textView.textContainer.lineFragmentPadding
// Create a bounding rect size by subtracting the padding
// from both sides and allowing for unlimited length
let boundingSize = CGSizeMake(textView.frame.size.width - padding * 2, CGFloat.max)
// Get the bounding rect of the attributed text in the
// given frame
let boundingRect = attributedText.boundingRectWithSize(boundingSize, options: NSStringDrawingOptions.UsesLineFragmentOrigin, context: nil)
// Compare the boundingRect plus the top and bottom padding
// to the text view height; if the new bounding height would be
// less than or equal to the text view height, append the text
if (boundingRect.size.height + padding * 2 <= textView.frame.size.height){
return true
}
else {
return false
}
}
As I have mentioned in my answer here, I advise against using shouldChangeCharactersInRange: since it is invoked before the text is actually changed.
Using the textViewDidChangeMethod: makes more sense, since it is invoked after the text actually changes. From there you can easily decide what to do next.
One options is to modify the textView yourself in the shouldChangeTextInRange delegate method and always return NO (because you already did the work for it).
- (BOOL)textView:(UITextView *)aTextView shouldChangeTextInRange:(NSRange)aRange replacementText:(NSString*)aText
{
NSString* oldText = aTextView.text;
NSString* newText = [aTextView.text stringByReplacingCharactersInRange:aRange withString:aText];
aTextView.text = newText;
if(/*aTextView contentSize check herer for number of lines*/)
{
//If it's now too big
aTextView.text = oldText;
}
return NO
}

Text character location changed when UITextView has Under line text in iOS 6

In my app i am giving command to user to create Bold,Italic and UnderLine text.
Bold and Italic text is coming perfectly.
and also Underline text working Perfectly in iOS 7.
But When used that code in iOS 6 the Underline will come under the text but the next character will come on range.location=0.
I just want the text go smoothly with UnderLine Text when User type.
My code for UnderLine Text is
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
NSLog(#"shouldChangeTextInRange %d - %d - %d",range.location,range.length,txtViewOfNotes.attributedText.length);
if (range.location==0) {
NSLog(#"location 0");
}
else{
NSLog(#"location 123");
NSMutableAttributedString *mat = [txtViewOfNotes.attributedText mutableCopy];
[mat addAttributes:#{NSUnderlineStyleAttributeName: #(NSUnderlineStyleSingle)} range:NSMakeRange (mat.length-1, 1)];
txtViewOfNotes.attributedText = mat;
}
}
OutPut for Example:
e
n
i
l
I am typing now txtUnder
Any one know why this is happening in only iOS 6.
You should just update the [textView textStorage] attributes rather than making a copy of the attributed string and replacing it.
So get rid of the mutableCopy line of code and replace the next line with:
[[textView textStorage] addAttribute:...]
Your code is completely replacing the textView's attributed string and as a consequence the cursor position is being reset. No need to replace it, just add attributes to the existing textStorage (which is already a mutableAttributedString).
See here for more on formatting text and images in UITextView and NSTextView.

Add \n to NSString or searching lines position in UITextView

I have plain NSString which i put into UITextView . I need to search position when new line starts. I think it can be done if textview converts string from
#"This is a big test string which i want to put into UITextView" to #"This is a\n big test \nstring which\n i want to\n put into\n UITextView" automaticaly. Then i can search for "\n" and find position of some line . Any idea how can i do it ?
That's not the way how UITextView works. The process of laying out text in a container is more complicated than just finding line breaks and does not produce a new string containing NL characters.
On iOS 7 you could use the TextKit properties (layoutManager, textContainer, ...) of UITextView to access the underlying layout components and query them for positions of parts of your string.
If you have to support older iOS versions there's characterRangeByExtendingPosition:inDirection: in the UITextInput protocol. You can use it to calculate the extent of a line of text.
I count "\n"s in an app to know when to hide the keyboard. instead of counting \ns you can count the other chars to get your "\n" location. Rows just counts the number of "\n"s, and is reset to zero on view did load.
// dismiss keyboard from text view with return key
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if([text isEqualToString:#"\n"]) {
rows++;
if ( rows >= 2 ){
// resign text view
[self.quotationTextView resignFirstResponder];
return NO;
}else{
return YES;
}
}else{
rows = 0;
}
return YES;
}

Resources