how to create textview singleline dynamically without increasing its height - iphone

I am using a textview and I want to fix the height to 30. I also want the text to scroll upwards when the text wraps to a new line. This also needs to be done dynamically. Any suggestions?

You will need to implement a couple of things:
You will need to implement a couple of UITextView delegate methods. Probably the easiest (at this point) for you would be to implement the
-(void)textViewDidChange:(UITextView *)textView{
NSRange range;
range.location = [textView.text length] - 1;
range.length = 1;
[textView scrollRangeToVisible:range];
}
method. With the code I have put in there. Let me know if this works for your needs :-)

Related

How can I get the UITextView to scroll only when it's full of text

I have this UITextView that works great except, I can't get the text inside the UITextView to start scrolling only after the UITextView's size in nearly full, the UITextView is 4 lines tall, but as soon as I reach the 2nd line the 1st line is pushed up, I don't want the view to begin scrolling until I've reached the 5 line. scrollingEnabled = NO keeps it from scrolling at all, so that didn't work.
UITextView *barf_ = [[UITextView alloc] initWithFrame:CGRectMake(20.0, 310.0, 155, 50)];
barf_.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
//[barf_ scrollRangeToVisible:NSMakeRange([barf_.text length], 0)];
barf_.layer.cornerRadius = 3.0f;
barf_.layer.borderWidth = 0.5f;
barf_.font = [UIFont fontWithName:#"Helvetica" size:13];
I found the answer, as others with similar problems have mention, with a small textView, it automatically adds 32 padding to the bottom.
A simple fix is to add YourTextView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0); inside shouldChangeTextInRange method, that fixed my problem!
Setting the contentInset may help the text to appear more correctly within the UITextView. However, it won't help solve the issue whereby the UITextView has scrolling enabled despite not having more text to view.
Similarly, methods such as sizeWithFont have limitations. As explained in Mike Weller's excellent blog series iOS Development: You're Doing It Wrong, NSString isn't a good object to ask regarding how large a UIView should be. Many UIView subclasses such as UILabel, UIButton, etc. have insets and other considerations that must be accounted for during sizing. UITextLabel is no exception.
Mike Weller's particular entry on this subject is:
You're Doing It Wrong #2: Sizing labels with -[NSString sizeWithFont:...]
iOS 7 promises us more sophisticated text handling in UITextView, with properties such as textContainerInset. But what to do in the meantime?
Well, first we know that UITextView is a subclass of UIScrollView. Therefore, the golden rule that if the contentSize is larger than the view's bounds property, the scroll view will scroll so we can see more content.
Checking out contentSize agains the bounds won't work either because we know that UIScrollView is already calculating whether it should scroll or not based on the text, and it's giving us the wrong answer.
This is where arbitrary adjustment values come to the rescue! For me this value was 17.f. For you - depending on your fonts - it maybe different. We then take control and decide whether we should allow the scroll view to scroll:
static const CGFloat kArbritaryHeight = 17.f;
CGFloat adjustedContentHeight = myTextView.contentSize.height - kArbritaryHeight;
CGFloat boundsHeight = CGRectGetHeight(myTextView.bounds);
BOOL tooMuchContent = adjustedContentHeight > boundsHeight;
if (tooMuchContent)
{
myTextView.scrollEnabled = YES;
}
else
{
myTextView.scrollEnabled = NO;
}
When your UITextView is loaded set scrollEnabled to NO. Then set the text view's delegate to self or some other object and implement the UITextViewDelegate method
- (void)textViewDidChange:(UITextView *)textView
This method will get called anytime the user makes a change to the text inside the view. Inside this method you need to figure out how big your text is and if it goes beyond the bounds of the text view. If so you enable scrolling. Use this method:
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
This is a UIKit category method on NSString. It returns a CGSize that will tell you the height of whatever text string you call it on. In your case it would be something like
CGSize textSize = [textView.text sizeWithFont:textView.font
constrainedToSize:CGSizeMake(textView.frame.size.width, MAXFLOAT)
lineBreakMode:UILineBreakModeWordWrap];
if (textSize.height > textView.frame.size.height) {
textView.scrollEnabled = YES;
} else {
textView.scrollEnabled = NO;
}
You might use the sizeWithFont:constrainedToSize:lineBreakMode: method to check whether your string will actually render larger than your text view and see if you need to enable scrolling. You will have to call it any time the text in your scrollview is set, however.
ex:
CGSize barfStringSize = [barfString sizeWithFont:[barf_ font]
constrainedToSize:CGSizeMake(barf_.bounds.size.width, MAXFLOAT)
lineBreakMode:UILineBreakModeWordWrap]
[barf_ setScrollEnabled:barfStringSize.height > barf_.bounds.size.height]

UItextView arabic text aligned to right

Using my custom arabic keyboard on UItextView inputView, I m filling my textView with the arabic text but cannot get the written text align to right....Need help to align text to right.
-(BOOL)textViewShouldBeginEditing:(UITextView *)textView{
if(showCustomKeyboard==NO){
[textView resignFirstResponder];
textView.inputView=nil;
[textView becomeFirstResponder];
return YES;
}
else{
[textView resignFirstResponder];
if(customKeyboard==nil){
customKeyboard=[[CustomKeyboard alloc] initWithFrame:CGRectMake(0, 264, 320, 216)];
[customKeyboard setDelegate:self];
}
if([[UIApplication sharedApplication] respondsToSelector:#selector(inputView)]){
if (textView.inputView == nil) {
textView.inputView = customKeyboard;
[textView becomeFirstResponder];
}
}
self.customKeyboard.currentField=textView;
[textView becomeFirstResponder];
}
return YES;
}
You can set the writing direction of a UITextView using the setBaseWritingDirection selector:
UITextView *someTextView = [[UITextView] alloc] init];
[someTextView setBaseWritingDirection:UITextWritingDirectionLeftToRight forRange:[someTextView textRangeFromPosition:[someTextView beginningOfDocument] toPosition:[someTextView endOfDocument]]];
The code is a little tricky because UITextView supports having different parts of the text with different writing directions. In my case, I used [someTextView textRangeFromPosition:[someTextView beginningOfDocument] toPosition:[someTextView endOfDocument]] to select the full text range of the UITextView. You can adjust that part if your needs are different.
You may also want to check whether the text in your UITextView is LTR to RTL. You can do that with this:
if ([someTextView baseWritingDirectionForPosition:[someTextView beginningOfDocument] inDirection:UITextStorageDirectionForward] == UITextWritingDirectionLeftToRight) {
// do something...
}
Note that I specified the start of the text using [someTextView beginningOfDocument] and searched forward using UITextStorageDirectionForward. Your needs might differ.
If you subclass UITextView replace all these code samples with "self" and not "someTextView", of course.
I recommend reading about the UITextInput protocol, to which UITextView conforms, at http://developer.apple.com/library/ios/#documentation/uikit/reference/UITextInput_Protocol/Reference/Reference.html.
Warning about using the textAlignment property in iOS 5.1 or earlier: if you use it with this approach together with setting the base writing direction, you will have issues because RTL text when aligned left in a UITextView actually aligns to the right visually. Setting text with an RTL writing direction to align right will align it to the left of the UITextView.
Try textAlignment property.
textView.textAlignment = UITextAlignmentRight;
Take a look at UITextView Class Reference.
EDIT: Maybe CATextLayer can help you, someone suggests to use this class to customize text, but I've never used it personally...
Otherwise, you can force your textView to reverse your input in UITextFieldDelegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
The text field calls this method whenever the user types a new character in the text field or deletes an existing character.
Here you can replace your input with a new NSString where you put the characters from right to left.
Hope this makes sense...
Ah... Do not forget to set
textView.textAlignment = UITextAlignmentRight;
to move your prompt on the right.
Try this code:
yourtextview.textAlignment = UITextAlignmentRight;
Hope this helps you.
Something which no one mentioned here or on any other post is that make sure you have not called sizeToFit for TextView. It simple aligns the textView (not text) to the left which gives the illusion that text is left to right instead of right to left.
If you are creating UI from Storyboard, the set constraint to Lead or Trailing space and value of First Item will be Respect Language Direction
in swift you can use this code
textView.makeTextWritingDirectionRightToLeft(true)

Text in UITextField moves up after editing (center while editing)

I have a strange problem. I have an UITextField in which the user should write the amount of something, so the field is called "amountField". Everything looks fine, when the user starts editing the textfield the text is in the vertical and horizontal center - that's great.
However, when the user ends editing the text moves up a little bit. I tried many things, nothing helped...
I am adding screenshots below, so you can see what is the problem.
This is what it looks like while editing the field - that's ok.
And this is how it looks when done editing - that is the problem!
Please, if anybody know what could cause this I would be very grateful! :)
Here is some of my code related to the amountField.
amountField.keyboardType = UIKeyboardTypeNumberPad;
amountField.returnKeyType = UIReturnKeyDone;
amountField.delegate = self;
[amountField setFont:[UIFont fontWithName:#"Nuptial Script LT Std" size:30]];
amountField.borderStyle = UITextBorderStyleNone;
UIImage *amountBg = [UIImage imageNamed:#"skin2_ipad_amountField.png"];
[amountField setBackground:amountBg];
amountField.rightView = nil;
//amountField.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.2];
amountField.textAlignment = UITextAlignmentCenter;
amountField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
amountField.adjustsFontSizeToFitWidth = YES;
amountLabel.textColor = UIColorFromARGB(0x313030); //Using my own macro
amountField.frame = CGRectMake(300, 480, 136, 32);
amountField.center = CGPointMake(605, 439);
PS: Those white corners are there because I set the background to white with 0.2 alpha, that's ok.
I had a similar issue that started happening on iOS 9. Basically I have a UITextField in a collection view cell. Sometimes when the user is done typing and editing ends, the text "bounces" up then down again into its correct position. Very strange and annoying glitch. Simply making this tweak fixed the issue on iOS 9 and proved to be safe on iOS 7 and 8:
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[textField layoutIfNeeded]; //Fixes iOS 9 text bounce glitch
//...other stuff
}
So...
After many hours of trying many things - I have found the problem.
In my case the problem is the font. I really don't know why, but the author of the font made the font weird (leading etc.), it has a blank space on the bottom. I don't know why, but when you are editing the text all of the text properties are ignored, but after you finish editing, they are applied.
So, if you have a similar problem, try changing the font to Arial or something similar.
For a full explanation, please consult these following links: link 1 and link 2. The solution recommended in these links can avoid you a lot of headaches and can even be applied to fix problem like text moving to the top when you start editing an UITextField (using System font or other particular fonts).
Disabling ClipsToBounds for the TextField solved it for me.
This bug happened to me when I set text & became the first responder in viewDidLoad or viewWillAppear. When I moved the becomeFirstResponder code to viewDidAppear the bug went away.
I'm struggling with this issue almost every time when the design of app is with custom font. One option is to fix the font (but this is too much work – at least for me :) ). The second option I'm using is subclassing the UITextField and overriding the editingRectForBounds: and placeholderRectForBounds: methods and correct the offset. It should work for your case too.
#implementation MyTextFieldWithFixedFontPosition
-(CGRect)editingRectForBounds:(CGRect)bounds{
return CGRectOffset([self textRectForBounds:bounds], 0, 0.5); //0.5 is just example, you can adjust to any offset you like
}
-(CGRect)placeholderRectForBounds:(CGRect)bounds{
return [self editingRectForBounds:bounds];
}
#end
I haven't tested it with leftView or rightView though, so be careful when using these :)
NOTE: this approach is "font dependant", values used for offset may vary for each font and size
There is a glitch on iOS 8.1 and below, I do not know if they will fix it later but at that time there is not an unique solution which fixes all cases, because the bug and the solutions are font's type, size dependent.
One of this solution or a combination of these solutions below can fix your problem:
Changing the font's size.
Changing the font's type.
Changing the UITextField's size.
Decompiling the font in question, modifying the font's characteristics and recompiling it (for more explanation please consult the following links: link 1 and link 2).
Otherwise this other self-sufficient solution below can fix your problem:
Swift version
import UIKit
class CustomTextField: UITextField {
...
override func textRectForBounds(bounds: CGRect) -> CGRect {
// Possible values.
return CGRectInset(bounds, CGFloat(35.0), CGFloat(0.0))
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
// Possible values.
return CGRectInset(bounds, CGFloat(35.0), CGFloat(0.0))
}
override func placeholderRectForBounds(bounds: CGRect) -> CGRect {
// Possible values.
return CGRectInset(bounds, CGFloat(35.0), CGFloat(0.0))
}
}
This solution has been tested with leftView and works like a charm.
NOTE: this approach is "font dependant", values used for CGRectInset may vary for each font and size.
I fixed this by adding height constraints to my UITextFields.
I wasn't able to change the font file, so when I solved this I saved the original UITextField's frame in a property and applied the following code:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
textField.frame = self.usernameFrame;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
textField.frame = CGRectOffset(self.usernameFrame, 0, 1);
}
It's a bit hacky, but it gets the job done.
I had similar issues with a UITextfield embedded in a UITableViewCell. Where exactly is this code located in your project? What I believe is happening is that after you've finished editing a particular textfield, it sends itself -setNeedsDisplay and its drawRect: is subsequently called. This might explain the shift in alignment. In my particular scenario, I had to use a table view delegate method -willDisplayCell... to set the content alignment. I would have to know more about your design to possibly offer a suggestion.
One potential solution would be to use the text field delegate methods to set the content alignment.
-(void)textFieldDidEndEditing:(UITextField *)textField{
if (amountField == textField){
amountField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
}
}
Check the keyboard to change the view of the position of the pop-up if there is line of code self.view.layoutIfNeeded() Delete it.Good Luck!
This is because the BaselineOffset for the textfield got changed.
In UITextFieldDidEndEditing creating an attributed text with NSBaselineOffset: 0 and using that attributedText would fix the problem.
-(IBAction)txtFieldDidEndEditing:(UITextField *)sender {
NSDictionary *style = #{
NSBaselineOffsetAttributeName: #(0)
};
self.txtField.attributedText = [[NSAttributedString alloc] initWithString:self.txtField.text attributes:style];
}
These solution above doesn't work for me.My solution is subclass UITextField and override setText:
- (void) setText:(NSString *)text {
[super setText:text];
[self layoutIfNeeded];
}
I used this extension. Only problem was I didn't add translatesAutoresizingMaskIntoConstraints = true before I called it. Ah silly goose
func centerVertically() {
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 5
let fittingSize = CGSize(width: bounds.width, height:
CGFloat.greatestFiniteMagnitude)
let size = sizeThatFits(fittingSize)
let topOffset = (bounds.size.height - size.height * zoomScale) / 2
let positiveTopOffset = max(1, topOffset)
contentOffset.y = -positiveTopOffset
}

Create UIButton on substring with help of NSRange

I have some text coming from server. It may be single line or multiline text. I have to display the text on UILabel, which is no problem for me. The problem is, I have to display UIButton on finding a particular substring of the same text. For example the text is Nitish\n435-234-6543\nIndia which is being displayed as follows :
Nitish
435-234-6543
India
So, when I find 435-234-6543 I have to display UIButton on 435-234-6543.
Notes:
The text is dynamic - coming from server. Above is only an example.
UIButton will be a subview of UILabel.
I tried different ways like OHAttributedLabel, rectForLetterAtIndex and this too. But not getting success. What my idea is, to create the button when substring is found and to set the frame of button based on NSRange of substring. Is this a possibility? How can it be done? Or is there some other way to do this?
I guess it is the approach I am worried about.
-->I have tried to calcluate position but didnt get success. Seems lots of work to calcualate position. One immediate solution come to my mind is why are you not taking one or two labels and one button.
Suppose. You got dynamic string from webservice is: "Nitesh-56789-Test".
Find range of string suppose i.e. "56789".
String before starting location of that range assign to one label i.e. assign "Nitesh" to lable one.
Now add one custom button with our searched string as a text(56789).
Now make sure in main string there something after our substring or not. Here I mean after our search string "56789" still "Test" remain so assign it to third lable.
Here you have to figue out frame of all labels and button using dynamic height width calculation by using sizeWithFont method.
1) Easy solution:
Make your UILabel a UITextView and use the property dataDetectorTypes to have phone numbers as links automatically.
2) More involved solution:
There is a convenient method to determine the size any text will need to be drawn:
CGSize size = [label.text sizeWithFont:label.font
constrainedToSize:CGSizeMake(label.frame.size.width, CGFLOAT_MAX)
lineBreakMode:UILineBreakModeWordWrap];
You could now determine which field is the phone number by splitting the string into its lines with:
NSArray *comp = [label.text componentsSeparatedByString:#"\n"];
and then checking which one is numeric. Now you would have to calculate the exact frame from the height of your size variable, maybe like this:
CGFloat positionOfNumber; // the index of your line in comp cast to CGFloat
CGFloat buffer = 10; // fiddle with this
CGFloat buttonHeight = (size.height- 2*buffer)/[comp length];
CGFloat buttonY = buffer + positionOfNumber * buttonHeight;
CGRect buttonFrame = CGRectMake(0, buttonY, label.frame.size.width, buttonHeight);
For those who want perfect solution here's the solution on how to get CGRect of a substring.

String length with given font to fit UITextView

I need to move text that the user has entered into a large multi-line UITextView into a smaller (but still multi-line) UITextView*. If the user has entered more text than will display in the smaller view, I want to truncate the text so that it fits with all the (truncated) text visible. (Neither the large UITextView nor the smaller one should scroll.)
What's the best way to do this?
I can use a loop, shortening the string by a character each time, and then use NSString's sizeWithFont: constrainedToSize: lineBreakMode: to find out the height this shorter string would need, and then compare that against the height I have available in my smaller UITextView, ending the loop when the string will fit - but that seems slow and awkward. There must be a better way.
I'd like to just tell the destination UITextView to truncate its displayText member as it displays it on screen, but I've not been able to find a way to do that.
*More context on this, from a comment I made below:
I've got a landscape app. I change the layout of the view depending on the photo the user chooses. If it's a landscape photo, the caption is smaller - just a line at the bottom of the photo. If she chooses a portrait photo, then there's plenty of space I can use for the caption at the side of the photo, so the caption is bigger.
If the user changes her photo orientation from portrait to landscape, then I want to truncate the text and then allow her to edit it so that it makes sense. I could just zap it, but I'd prefer to preserve it to minimize her typing.
I wrote the following recursive method and public API to do this properly. The ugly fudge factor is the subject of this question.
#define kFudgeFactor 15.0
#define kMaxFieldHeight 9999.0
// recursive method called by the main API
-(NSString*) sizeStringToFit:(NSString*)aString min:(int)aMin max:(int)aMax
{
if ((aMax-aMin) <= 1)
{
NSString* subString = [aString substringToIndex:aMin];
return subString;
}
int mean = (aMin + aMax)/2;
NSString* subString = [aString substringToIndex:mean];
CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight);
CGSize stringSize = [subString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
if (stringSize.height <= self.frame.size.height)
return [self sizeStringToFit:aString min:mean max:aMax]; // too small
else
return [self sizeStringToFit:aString min:aMin max:mean];// too big
}
-(NSString*)sizeStringToFit:(NSString*)aString
{
CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight);
CGSize stringSize = [aString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
// if it fits, just return
if (stringSize.height < self.frame.size.height)
return aString;
// too big - call the recursive method to size it
NSString* smallerString = [self sizeStringToFit:aString min:0 max:[aString length]];
return smallerString;
}
This isn't actually a fix but it does provide a good starting poing for the calculation.
If you use NSString's sizeWithFont: constrainedToSize: lineBreakMode: you get a vertical height for your text. If you divide that by your font's leading height, you get the number of lines in the whole string. Dividing [NSString count] by that number gives you an approximation to number of characters per line. This assumes the string is homogeneuous and will be inaccurate if someone types (e.g.) 'iiiiiiiiiii..." as oposed to "MMMMMMMMM...".
You can also divide you bounding box by the relevent font's leading height to get the number of lines that fit within your bounding box.
Multiplying characters per line by number of lines gives you a starting point for finding text that fits.
You could calculate the margin for error in this figure by doing the same calculation for those 'iiiiii...' and "MMMMMM...'" strings.
I would suggest taking a slightly different approach and seeing if you can use a UILabel instead of the smaller UITextView.
UILabels can be setup to be multi-line like a UITextView through their numberOfLines property.
UILabels also have a lineBreakMode property and I believe that the default value of that property will do the exact truncation effect that you are looking for.
I think Jonathan was on to something about the UILabel...
So, the user finishes editing the UITextView, you get the string of text and pass it to the UILabel. You change the alpha of the UITextView to 0 and/or remove it from superview. Possibly store the untruncated full text in an ivar.
UILabels are not "editable", however you can detect a touch with a UILabel (or it's superview).
When you detect the touch on the UILabel, you simply restore the hidden UITextView and restore the string you saved.
Sometimes the SDK is a pain, but it almost always wins the fight. Many times, it is better to adjust your design to UIKit conventions