Using information I've gathered from multiple answers I found here on stackoverflow, I've been able to correctly size a UITextView's height using:
[bookQuoteTxtView sizeToFit];
[bookQuoteTxtView layoutIfNeeded];
I'm doing this because the UITextView will obviously contain different amounts of text as a result of what the user may have selected on a previous screen.
However, I'm still running into one strange problem.
It seems that when the text that gets passed into the UITextView contains a line-break, it throws the whole height thing off.
Here's the text without any line-breaks:
And here's the same text with a couple of line-breaks thrown in the middle:
As you can see (with the help of the orange & yellow background colors I added to the UITextView & ScrollView that contains it), the UITextView's height didn't resize appropriately and now the text gets cut off - all because of those linebreaks.
The code I'm using to do all this dynamic height-changing on the UITextView is:
// BookQuote TextView:
bookQuoteTxtView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 310, 80)];
bookQuoteTxtView.backgroundColor = [UIColor orangeColor];
bookQuoteTxtView.font = [UIFont fontWithName:#"Arial" size:15];
bookQuoteTxtView.text = [NSString stringWithFormat:#"%#", bookObject.bookQuote];
[bookQuoteTxtView sizeToFit];
[bookQuoteTxtView layoutIfNeeded];
bookQuoteTxtView.editable = NO;
[scroller addSubview:bookQuoteTxtView];
// Now get the HEIGHT of the Book-Quote TextView:
CGRect textViewFrame = bookQuoteTxtView.frame;
CGFloat textViewFrameHeight = bookQuoteTxtView.contentSize.height;
textViewFrame.size.height = textViewFrameHeight;
bookQuoteTxtView.frame = textViewFrame;
So again, this works almost flawlessly for just about every string that gets passed into my UITextView box. Thus, different strings of different lengths all work, the UITextView's height adjusts automatically and everything's cool. Its just the line-breaks that throw everything off.
HAVING SAID THAT...
Its seems that line-breaks generated using "\n" in the string DO work, but line breaks that are embedded/encoded in a string that's coming in and being read from an Sqlite3 database - do NOT.
So you gotta wonder if that's where the problem lies.
Here's the code I use to read my date from the Database:
// Book Quote:
char *bookQuoteChar = (char *)sqlite3_column_text(statement, 4);
NSString *bookQuoteString;
if (bookQuoteChar == NULL) {
bookQuoteString = #"N/A";
}
else {
bookQuoteString = [[NSString alloc] initWithUTF8String:bookQuoteChar];
}
I'm thinking more and more that its the database that's causing the problem, but I'm presenting the entire picture here in its totality to make sure I'm still doing the height-readjustment stuff correctly.
Please let me know if something's jumping out at you or if there are any suggestions for fixes.
I can't really suggest much besides using a technique to set the contentSize of the UITextView.
textView.text = #"text from DB";
CGRect rect = textView.frame;
rect.size.height = textView.contentSize.height;
textView.frame = rect;
and then use any techniques that may be needed to additionally update the frame. Hope you solve the problem!
Related
This is what i have tried,
UITextView *_textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, 10)];
NSString *str = #"This is a test text view to check the auto increment of height of a text view. This is only a test. The real data is something different.";
_textView.text = str;
CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;//Here i am adjusting the textview
[self.view addSubview:_textView];
Basically after fitting the text into textview,scrolling is enable,but i cannot view the content inside the textview without scrolling the textview.I do want to initialize the UITextView frame size based on the text size,font name etc.
Any solution is appreciated.Thanks.
NSString *str = #"This is a test text view to check the auto increment of height of a text view. This is only a test. The real data is something different.";
UIFont * myFont = [UIFont fontWithName:#"your font Name"size:12];//specify your font details here
//then calculate the required height for the above text.
CGSize textviewSize = [str sizeWithFont:myFont constrainedToSize:CGSizeMake(300, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
//initialize your textview based on the height you got from the above
UITextView *_textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, textviewSize.width, textviewSize.height)];
_textView.text = str;
[self.view addSubview:_textView];
And also you want to disable the scrolling in textview then refer this.
As William Jockusch states in his answer here:
You can disable almost all scrolling by putting the following method
into your UITextView subclass:
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated {
// do nothing
}
The reason I say "almost" all scrolling is that even with the above,
it still accepts user scrolls. Though you could disable those by
setting self.scrollEnabled to NO.
If you want to only disable some scrolls, then make an ivar, lets call
it acceptScrolls, to determine whether you want to allow scrolling or
not. Then your scrollRectToVisible method can look like this:
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated {
if (self.acceptScrolls)
[super scrollRectToVisible: rect animated: animated];
}
I asked a developer (TwoLivesLeft, the creators of Codea) how they did syntax highlighting in their app. He replied :
#TD2 the Codea editor is implemented using a UITextView. The
highlighting is done by overlaying subviews in the appropriate
positions — usually UILabels. They are dequeued from a re-use pool,
similar to the way UITableViewCells work. During scrolling, the lines
requiring re-highlighting pull markers out of the pool and lines that
have moved off screen dump their markers back into the pool.
Can anyone explain how I would get the x and y of a certain word?
UITextView conforms to UITextInput, of which a detailed description can be found here.
Take a look at the required methods "textRangeFromPosition:toPosition:", "positionFromPosition:offset:", "positionFromPosition:inDirection:offset:", and some of the other geometric-based methods in the UITextInput Protocol. Those might provide the functionality you are looking for.
I have not actually tried to make sure these work the way you want them too, but that looks like its about what you need.
Let me know if you need any more help!
UPDATE:
Here is some sample code of how to do this. I ended up getting the "firstRectForRange:" method to work. This code basically takes the last three letters of the UITextView "textStuff" and highlights it green.
UITextView *textStuff = [[UITextView alloc] init];
textStuff.frame = CGRectMake(2.0, 200.0, 200.0, 40.0);
textStuff.text = #"how are you today?";
textStuff.textColor = [UIColor blackColor];
UITextPosition *Pos2 = [textStuff positionFromPosition: textStuff.endOfDocument offset: nil];
UITextPosition *Pos1 = [textStuff positionFromPosition: textStuff.endOfDocument offset: -3];
UITextRange *range = [textStuff textRangeFromPosition:Pos1 toPosition:Pos2];
CGRect result1 = [textStuff firstRectForRange:(UITextRange *)range ];
NSLog(#"%f, %f", result1.origin.x, result1.origin.y);
UIView *view1 = [[UIView alloc] initWithFrame:result1];
view1.backgroundColor = [UIColor colorWithRed:0.2f green:0.5f blue:0.2f alpha:0.4f];
[textStuff addSubview:view1];
Result of running this code:
#bddicken's https://stackoverflow.com/a/11487125/3549781 works like a charm. But the problem is, it doesn't work on iOS 7+ if the text contains a newline "\n". After a lot of searching I found a solution for that.
You have to ensure the layout of textView before calling firstRectForRange: by
[textView.layoutManager ensureLayoutForTextContainer:textView.textContainer];
Courtesy : UITextView firstRectForRange not working when there's new line characters in the mix
P.S : At first I added this as a comment to #bddicken's answer. As most people don't read comments I added this as an answer.
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]
I have a view with a scrollview. I use code to add labels to the scrollview. Since there's a lot of redundant code, I tried to move the label creation to a separate function that returns a UILabel. Here' that function:
- (UILabel *) f_MakeLabelWithL:(float)MyLeft T:(float)MyTop W:(float)MyWidth H:(float)MyHeight Align:(UITextAlignment)MyAlign
Font:(UIFont *)MyFont TextColor:(UIColor *)MyTextColor BGColor:(UIColor *)MyBGColor {
CGRect rect = CGRectMake(MyLeft, MyTop, MyWidth, MyHeight);
UILabel *label = [[[UILabel alloc] initWithFrame:rect] autorelease];
label.adjustsFontSizeToFitWidth = YES;
label.backgroundColor = MyBGColor;
label.font = MyFont;
label.numberOfLines = 0;
label.textAlignment = MyAlign;
label.textColor = MyTextColor;
return label;
}
I set the text of the label after it is returned. Or so I intended. In fact, a label that is told to have a blue background appears as a solid black rectangle. One with a clear background is entirely clear. Worse than that: the scrollview won't scroll, and attempting to make it do so crashes the app without any explanation in the debugger console.
Yet all this code works inline just fine and dandy.
Anyone know why?
Update: Putting the code inline fixed the display of the labels. But I was also using a function to create images:
- (UIImageView *) f_MakeImageWithL:(float)MyLeft T:(float)MyTop W:(float)MyWidth H:(float)MyHeight File:(NSString *)MyFile {
CGRect rect = CGRectMake(MyLeft, MyTop, MyWidth, MyHeight);
UIImageView *oImageView = [[[UIImageView alloc] initWithFrame:rect] autorelease];
NSString *s = [[Isystant f_DocumentsPath] stringByAppendingPathComponent:MyFile];
UIImage *oImage = [UIImage imageWithContentsOfFile:s];
[oImageView setImage:oImage];
oImageView.backgroundColor = [UIColor redColor];
oImageView.opaque = YES; // explicitly opaque for performance
return oImageView;
}
It was creating the images, but the frozen scroll/crash problem didn't go away until I put this code back inline also.
Looks to me like you are over-releasing the UILabel. You shouldn't have to release the label after adding it to your scrollview if it was never retained anywhere prior to that (and you've autoreleased it in the makeImageWithL method).
That's probably why it is working when you put it inline... because in that scenario, I would bet you are removing the autorelease, right?
-S
Do you retain your returned Label somewhere?
I am using a TTStyledTextLabel in my project to parse the hyperlinks, it all works fine. The only problem I am facing is to truncate a long text - show ellipses if the text does not fit in the bounds of TTStyledTextLabel.
In other terms, I need the same behavior as a UILabel which adds ellipses to indicate that some text is clipped. I have searched in TTStyledTextLabel and TTStyledText classes, there is no provision to achieve this. The following is the code which I have used in my UITableViewCell subclass to set the frame of TTStyledTextLabel appropriately:
-(void) layoutSubviews
{
[super layoutSubviews];
.
.
.
CGSize maxSize = CGSizeMake(self.contentView.frame.size.width -TEXT_OFFSET_WIDTH, TT_TEXT_MAX_HEIGHT);
[[[self textLabelTTStyled] text] setWidth:maxSize.width];
[[self textLabelTTStyled] sizeToFit];
double heigthForTTLabel = [[[self textLabelTTStyled] text] height];
if (heigthForTTLabel > maxSize.height)
heigthForTTLabel = maxSize.height; // Do not exceed the maximum height for the TTStyledTextLabel.
**// The Text was supposed to clip here when maximum height is set!**
CGSize mTempSize = CGSizeMake([[[self textLabelTTStyled] text] width], heigthForTTLabel);
CGRect frame = CGRectMake(TEXT_OFFSET_X,TEXT_OFFSET_Y,mTempSize.width, mTempSize.height);
self.textLabelTTStyled.frame = frame;
.
.
.
}
And in the tableView:cellForRowAtIndexPath: I am setting text like this to my TTStyledTextLabel:
TTStyledText *styledStatusMessage = [TTStyledText textFromXHTML:#"This is a really long text, how long can this go on? This is a really long text, how long can this go on? This is a really long text, how long can this go on? This is a really long text, how long can this go on? This is a really long text, how long can this go on? This is a really long text, how long can this go on?"
lineBreaks:YES URLs:YES];
if (nil == styledStatusMessage) {
styledStatusMessage = [TTStyledText textWithURLs:[statusMessage title] lineBreaks:YES];
[[cell textLabelTTStyled] setText:styledStatusMessage];
}
The excessive text is simply being discarded, the ellipses are not added by default to indicate that text is clipped. Any solutions to this problem?
Thanks,
Raj
I believe you are using a private api Three20 which is having a chance oof rejection from appstore.Just check once before you upload.
Genrally you can set any set any custom button with setting title on it
[button setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 10.0, 0.0, 0.0)]; - This line can be used for settting title edges. Hope it helps.