iOS: Appending an uneditable string to the end of a UITextView - ios

I would like to reproduce a screen similar to the Facebook "Update Status" view in iOS.
(This text should be editable) Walking (Anything past here should not be editable) - at South Narrabeen Beach
The user should be able to enter/edit text to the left of the appended string. The appended string will need to wrap within its parent and be clickable.
Does anyone know how this is done? (I have recently seen it in the Viddy app as well).
Could it be a growing UITextField with a UIAttributedString split over 2 lines that updates its frame as the text is entered?

Update:
It looks like what you want is to let the user place the cursor in the signature anyway, but not let her type
In that case, you would want to use this instead
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
NSInteger signatureLength=20;
if(range.location>self.textView.text.length-signatureLength){
return false;
}
else{
return true;
}
}
Original:
You need to use UITextViewDelegate
Implement the - (void)textViewDidChangeSelection:(UITextView *)textView method, something like:
For this example, let's assume the signature length is 20, this would look something like this:
-(void)textViewDidChangeSelection:(UITextView *)textView{
NSInteger signatureLength=20;
NSRange newSelection=self.textView.selectedRange;
if(newSelection.location>self.textView.text.length-signatureLength){
[self.textView setSelectedRange:NSMakeRange(self.textView.text.length-signatureLength, 0)];
}
}
So basically you intercept every time the selection (== the cursor in this case) changes, and if the cursor is going to be in the middle of the signature, you reposition it just before.
Setting a selection with a 0 length just changes the cursor position.

Related

How to count the number of characters in a text field dynamically in IOS

I want to be able to calculate 200 - the number of characters at any time.
Meaning that I want to display the number of characters and have it update any time the user inputs or deletes characters.
The code I have properly counts 200 - the number of characters at any time but I don't know how to update the text field constantly.
-(void)updateLabel{
// Get the text from the message
// find the length of the text
// subtract from the converted number in the charCounter Label
// display in the charCounter Label
int length = _message.text.length;
int charLeft = 200 - length;
NSString* charCountStr = [NSString stringWithFormat:#"%i", charLeft];
_charCounter.text = charCountStr;
}
And then I call the updateLabel function in the viewDidLoad.
I suspect that there must be some other function to persistently update the viewController
Just implement the UITextFieldDelegate method textField:shouldChangeCharactersInRange:replacementString: and call your updateLabel method from within it.
From the docs:
The text field calls this method whenever the user types a new character in the text field or deletes an existing character.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
[self updateLabel];
return YES;
}
Don't forget to set your text field's delegate property, and to make sure that your class conforms to UITextFieldDelegate.
It seems that you want a Twitter-like dynamic count of the amount of characters that a user can type into a UITextBox. So you have a limit of 200 characters and you want to inform the user how much characters he or she typed dynamically.
If you want that method to run every time the the text changes:
// textField is the UITextField you type your text in to.
// this could be a reference to that field via an IBOutlet or you created it manually
[textField addTarget:self
action:#selector(updateLabel)
forControlEvents:UIControlEventEditingChanged];

How to get n lines of text from UITextView in 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.

How to get the cursor in an UITextView to reflect an updated typingAttributes for paragraph alignment?

I am using a UITextView to enter some rich text. I have created a button to change the paragraph alignment (left, center, right).
I am applying the alignment to the attributedText when the user selects some text and it works as expected.
However, when the user hit return and is in a new, zero-length paragraph (just after a newline and nothing following), I believe I should change the typingAttributes to reflect the attributes I want the new text to receive.
I used the following code:
if ((paragraphRange.length == 0) && (paragraphRange.location == [mutableText length])) {
NSMutableParagraphStyle * mutableParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[mutableParagraphStyle setAlignment:textAlignment];
[mutableParagraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
NSMutableDictionary * attributes = [self.typingAttributes mutableCopy];
[attributes setObject:mutableParagraphStyle forKey:NSParagraphStyleAttributeName];
self.typingAttributes = attributes;
It does apply the paragraph alignment once the character is typed, but the cursor in the UITextView does not reflect the change until after the character is typed. I am looking for a mechanism to get the cursor into the right place before the text is typed. For example, if the user selects 'centered', I want the cursor to move to the center of the view to show where the text will do.
Anyone have any ideas on how to do this?
Thanks in advance,
Charlie
For those tracking this, it looks like this is a bug in iOS7. The renderer does not appear to take into account the state of the typingAttributes paragraph alignment when computing the location of the cursor.
Looks like this one needs a bug report to Apple ;-)
Here is how I am doing it and this causes the cursor to move immediately. I think the key is to set the attribute on the whole paragraph. There does seem to be a bug where sometimes the cursor does not move but if you scroll the textview then that causes it to move. Seems to only occur near the top of the uitextview (lines 1 - 5) and if the two styles use the same font size.
There is a similar bug if you select text and change it to or from bold the selected area is not resized to reflect the new width of the changed text. Also if you programatically change the Font to bold for a paragraph then the cursor position is not updated to fit the changed width of the text. However if you also change the text point size the cursor is correctly repositioned for the next text width.
/*! Applies the Normal style to the range returned by [self rangeForUserParagraphAttributeChange]
#param sender The id of the control sending the message.
*/
- (IBAction) styleNormal:(id)sender
{
FLOG(#"styleNormal called");
NSRange charRange = [self rangeForUserParagraphAttributeChange];
if (charRange.location == NSNotFound) return;
NSTextStorage *myTextStorage = [self textStorage];
if ([self isEditable] && charRange.location != NSNotFound)
{
[myTextStorage beginEditing];
[myTextStorage setAttributes:[self normalStyle] range:charRange];
[myTextStorage endEditing];
}
[self setTypingAttributes:[self normalStyle]];
}
- (NSRange)rangeForUserParagraphAttributeChange {
NSRange paragaphRange = [self.textStorage.string paragraphRangeForRange: self.selectedRange];
return paragaphRange;
}
Did you try resigning the textView as first responder, then making it first responder right after that.
[textView resignFirstResponder];
[textView becomeFirstResponder];
Calling [textView setNeedsLayout] seems to fix the cursor location.

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;
}

Is it safe to call stringByReplacingCharactersInRange in UITextView's delegate textView:shouldChangeTextInRange:replacementText:?

I implemented a customized textview based on UITextView, trying to listen the text change event. So I implemented the UITextView's delegate:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
//It crashes!
NSString *result = [textView.text stringByReplacingCharactersInRange:range withString:text];
return YES;
}
In iOS 5 and iOS 6,the above code works fine. But in iOS 4.3, it crashes with the exception ***-[NSCFString replaceCharactersInRange:withString:]: Range or index out of bounds.
I printed all the variable values, found in iOS 4.3, sometimes the range is out of bound indeed: textView.text.length is 111, but the range is 113,0. What's wrong with my code? Thanks!
This method is automatically called whenever a change is made in a textview, and is very stable. My guess (as you confirmed in your comment above) is that you must be calling this method manually in your code, and providing an incorrect range.

Resources