While typing in a UITextView sometimes it scrolls down to current line(case a) but it doesn't the other times(case b).
There's another problem which is:
The same UITextView sometimes show all the text in it (case 1) but other times it doesn't show the last line of text(case 2).
Whenever case 1 happens case a follows.
and Whenever case 2 happens case b follows.
This is the hierarchy of the view:
Size(variable height-fixed width) of these UITextViews as well as UICollectionViewCells are calculated using sizeWithFont:constrainedToSize:lineBreakMode:
Limits of height are set from 43 to 120.
if height>43 then enableScrolling is set to YES, otherwise to NO(Logic X).
Scrolling is enabled when textViewBeginEditing and Logic X is applied when textViewEnded Editing.
There is no scrolling in case 2.
Please suggest cause and workarounds.
On iOS7, I think that you could leave the UITextView's scrollEnabled property set to YES in all cases. If it contains less text it will just not scroll. If you set the property while configuring the cell, you might get this kind of weird behavior, because UIKit is reusing those cells and probably the UITextView too.
For making the last line visible, I'm guessing you need to calculate the text view size more accurately. Try using the attributedText to set the text, with all the formatting you need. Then, in order to calculate the size, you can do it like this:
NSAttributedString *text = self.yourCellsTextView.attributedText;
UITextView *calculationView = [[UITextView alloc] init];
[calculationView setAttributedText:text];
CGSize size = [calculationView sizeThatFits:CGSizeMake(self.yourCellsTextView.frame.size.width, FLT_MAX)];
CGFloat finalMessageHeight = size.height;
Hope this helps :).
Related
I have a non-scrollable UITextView embedded in a UIScrollView and add text to the UITextView dynamically. The UIScrollView adjust it's contentSize accordingly based on the TextView's frame. However, once the UITextView exceeds a height of 8192, the text will become invisible (but still there, because you can use the magnifying glass to highlight text and even see parts of the text through the magnifying glass).
CGRect textviewFrame = self.TextView.frame;
textviewFrame.size.height = [self textViewHeightForAttributedText:self.TextView.attributedText andWidth:320.0];
self.TextView.frame = textviewFrame;
self.ScrollView.contentSize = self.TextView.frame.size;
Helper function to size UITextView accordingly:
- (CGFloat)textViewHeightForAttributedText:(NSAttributedString *)text andWidth:(CGFloat)width
{
UITextView *textView = [[UITextView alloc] init];
[textView setAttributedText:text];
CGSize size = [textView sizeThatFits:CGSizeMake(width, FLT_MAX)];
return size.height;
}
Didn't realize it was the same exact problem that was unsolved here until I tested it out explicitly by forcing the max size to 8193 and the problem occurred (while a max size of 8192 still had the text showing correctly). Anyone run into this problem before and know of a work around? Thanks
I was recently hit by this problem and have worked out an effective way around it. Although it seems like an iOS bug IMHO it's really not... there are practical limits to CALayer sizes, plus drawing an 8K high piece of text takes a long time. Much better to do as Apple intended and to only render the bit of text that's visible... that's why UITextView extends UIScrollView after all.
The problem is that UITextView isn't terribly easy to integrate with other bits of UI. In my case I am working on a news app where a single UITextView is used to render the article, plus there's some separate UI (titles and buttons etc) above and below it, all hosted in a single scrollable container.
The solution I've found is to split the UITextView into two views... a dedicated UITextView container whose frame is the full text size (i.e. the same size your UITextView's contentSize) and which is the superview of your UITextView. Your child UITextView's frame should be set to the bounds of the outer scrollable container.
Then all you have to do is use key-value observation to monitor the contentOffset property of your outer scrollable container view (in my case this is a UICollectionView). When its contentOffset changes you update (1) the contentOffset of your UITextView, and (2) the transform property of the UITextView's layer. By updating that transform property the UITextView is fixed to fill the currently-visible part of it's superview. But because you're also updating the UITextView's contentOffset, this trickery is totally invisible to the user... it looks and behaves as if the UITextView is simply very large.
Here's a fully functional solution, for anyone who'd like it!
** Assuming your content size will not exceed the limits of two text views **
This solution works by adding two UITextViews to your view, and splitting your text between them. It looks complicated, but it's actually very simple! I've just written a very verbose description :)
Step 1 - Add two UITextViews to your view:
I added mine in my storyboard. Place them so that one is directly above the other, with no space between them. Don't worry about setting the height (we will set that later).
Set constraints on the views so that they are tied to each other from the top and bottom, and the surrounding edges of the container from all other sides, including your desired padding. i.e. tie the first text view to the container from the top, left, and right, and to the second text view from the bottom. Tie the second text view to the container from the bottom, left, and right, and to the first text view from the top. This will ensure that they stretch appropriately when the content is set.
Don't set any constraints on the height of the views, or if you must (to avoid warnings from the constraints inspector), set the height of one of the views to be >= 20, or some similarly small number.
Disable scrolling, bouncing, and scrolling indicators for both your text views. This solution relies on the views being a fixed, non-scrollable height, so if you'd like your content to scroll, you should use a UIScrollView or UITableViewCell as a container.
Create outlets for your two new text views in your view controller file, naming them something like textView1 and textView2.
Step 2 - Set textContainerInsets to zero:
Set the textContainerInset property on both text views to zero, either using User Defined Runtime Attributes:
or code:
self.textView1.textContainerInset = UIEdgeInsetsZero;
self.textView2.textContainerInset = UIEdgeInsetsZero;
This will ensure that the no visible space will appear between the two views when the content is set, and should not affect the other spacing around your views.
Step 3 - Split your content, set it, and update the view heights:
Simply copy the following code into your view controller file (viewDidLoad), and set the contentString variable to your content.
/* Content is split across two UITextViews to avoid max drawing height */
NSString *contentString = #"Some very long piece of text...";
// Set text
NSArray *components = [contentString componentsSeparatedByString:#"\n"];
NSInteger halfLength = [components count] / 2;
NSArray *firstHalf = [components subarrayWithRange:NSMakeRange(0, halfLength)];
NSArray *secondHalf = [components subarrayWithRange:NSMakeRange(halfLength, [components count] - halfLength)];
NSString *contentString1 = [firstHalf componentsJoinedByString:#"\n"];
NSString *contentString2 = [secondHalf componentsJoinedByString:#"\n"];
self.textView1.text = contentString1;
self.textView2.text = contentString2;
// Set text view heights
CGFloat fixedWidth1 = self.textView1.frame.size.width;
CGFloat fixedWidth2 = self.textView2.frame.size.width;
CGSize newSize1 = [self.textView1 sizeThatFits:CGSizeMake(fixedWidth1, CGFLOAT_MAX)];
CGSize newSize2 = [self.textView2 sizeThatFits:CGSizeMake(fixedWidth2, CGFLOAT_MAX)];
CGRect newFrame1 = self.textView1.frame;
CGRect newFrame2 = self.textView2.frame;
newFrame1.size = CGSizeMake(fmaxf(newSize1.width, fixedWidth1), MIN(newSize1.height, 8192));
newFrame2.size = CGSizeMake(fmaxf(newSize2.width, fixedWidth2), MIN(newSize2.height, 8192));
self.textView1.frame = newFrame1;
self.textView2.frame = newFrame2;
This code splits the contentString roughly in the middle, looking for the nearest newline. If you'd like to split your content on a different character, simply change all occurrences of \n above to whatever you'd like to split on.
Step 4 - Set your container view height:
Set your container view (scrollView, tableViewCell, whatever else) to the height of your two text views, plus whatever additional space you've set above and below them.
CGRect viewFrame = self.myView.frame;
viewFrame.size.height = MIN(self.textView1.frame.size.height, 8192) + MIN(self.textView2.frame.size.height, 8192) + kTextViewContainerPadding;
[self.myView setFrame:viewFrame];
(In my code, kTextViewContainerPadding is a macro I've set to the sum of the space above and below my two text views, within their container).
That's it! Good luck.
Try enabling the scroll for the scrollView.
Keep the height of the textView > height of the content, so that in reality there will be no scroll, but scrollEnabled should be = YES
It solved the problem for me.
Hello I think am not late to answer. I got the same problem like you. This is my solution:
textView.scrollEnabled = YES;
contentTextView.attributedText = finalAttrString;
// contentTextView.text = [attrString string];
contentTextView.font = kFont(contentTextFS + [valueOfStepper intValue]);
[contentTextView sizeToFit];
contentTextView.height += 1;//This is the key code
//contentTextView.height = 8192.0f
Them I solved the trouble and I can change size dynamic.Successfull on iOS 8
i'm currently making an app where the suer selects an MKMapView annotation and then the title of the annotation(pin) is set to a detailtextLabel in a Right Detail UITableViewCell.
My Problem is that when the text is large, the detailTextLabel becomes multiple lines. When this happens the TextLabel of the cell(the one the left) shifts up. Heres What I've tried:
In the cellForRowAtIndexPath Method, I tried adjusting the frame through the following code:
CGRect frame = cell.textLabel.frame;
frame.origin.y = cell.frame.size.height/2;
cell.textLabel.frame = frame;
Where cell is a UITableViewCell that is set to right detail
Subclass the cell and tun try to adjust the frame in the -(void)layoutSubviews
How do I stop it from going up and keep it at the center of the cell?
If you want to do a custom layout of UITableViewCell, then you need to add your custom UI elements to its -[UITableViewCell contentView] instead of trying to modify geometry of standard UI elements that are created by default.
So, in your case, you need to add two UILabels and set their position so that:
Title label will not move at all
Detail text label will be also multiline
In this way you'll be able to solve this problem!
Please try to make the font size adjustable amount to the text.
I think you can use,
cell.detailTextLabel.adjustFontSizeToWidth = Yes;
And also set the numberOfLines to 0.
Hope that solves the purpose.
I have a tableview that displays a feed of statuses(pictured below) and im trying to figure out how to size each cell appropriately aka short statuses having a smaller cell and bigger cells that display all the status text for longer statuses. Ive come across this tut: http://www.cimgf.com/2009/09/23/uitableviewcell-dynamic-height/
but, im still a little confused on how I would go about calculating in the extra space for my buttons or the margin to the left for my thumb img.
currently it consists of 2 buttons, a label (for the users name) , a thumb img and a textfield (unsure if I should be using a textfield or label for displaying the status, either or is fine with me if i it works)
Please excuse me in advance if this is a question that has already been answered somewhere. I am primarly a Android developer just getting started in IOS and have found alot of mixed answers on this topic so im a little unsure if there is a simple solution to this problem in IOS7
There are several ways of doing this, but I've found that the most reliable is to use a temporary and invisible UITextView to get the necessary size.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Ideally you should do lazy loading so that instead of creating a new textView each time, you just reuse the same one.
UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 44)]; //This initial size doesn't matter
temp.font = __Your_desired_font__
temp.text = #"Put your status here";
CGFloat textViewWidth = __your_constraint_width__
CGRect tempFrame = CGRectMake(0,0,textViewWidth,44); //The height of this frame doesn't matter.
CGSize tvsize = [temp sizeThatFits:CGSizeMake(tempFrame.size.width, tempFrame.size.height)]; //This calculates the necessary size so that all the text fits in the necessary width.
//Add the height of the other UI elements inside your cell
return tvsize.height + staticElementHeights
}
This works on iOS 7, but I think if you want to support iOS 6 you need to actually add the textView as a subview for this to work. Just set temp.hidden = YES
The way text is displayed in a UILabel is different from a UITextView, so for a UILabel, you should use the method described in this answer
iOS auto adjust label height
I want to use auto-layout for UITableViewCells. These table cells have a dynamic height (depending on text length).
I'm using [UIView -systemLayoutSizeFittingSize:] to calculate the appropriate cell height (to return in [UITableView -heightForRowAtIndexPath:]) but I keep getting the following results:
If I pass UILayoutFittingCompressedSize, I get back a CGSize of (0,0).
If I pass UILayoutFittingExpandedSize, my app crashes with this error:
*** Assertion failure in -[NSISLinearExpression incrementConstant:], /SourceCache/Foundation_Sim/Foundation-1043.1/Layout.subproj/IncrementalSimplex/NSISLinearExpression.m:620
(My guess is that this means some number is infinite.)
My implementation is simple. I calculate the height for each object, and then cache it:
MessageCell *cell = // allocate a new cell...
// set up the cell (assign text, images, etc)
CGSize size = [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
self.cellHeight = size.height; // size always equals (0, 0)
I hypothesize that this is a problem with the constraints I set, but:
If I manually set cellHeight to a large value, the cells all look fine except the height is wrong.
Interface Builder gives me no warnings about ambiguous restraints
[cell hasAmbiguousLayout] returns NO.
My cell has, among other things, an image set at 48x48, so a size of (0, 0) shouldn't satisfy all the constraints.
Any ideas?
This is what works for me (note 'contentView')
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
EDIT: I answered a similar question and provided a complete example, here:
How to resize superview to fit all subviews with autolayout?
It is hard to say something concrete basing on your post because you didn't post constraints that you use.
Apple Documentation:
systemLayoutSizeFittingSize:
Returns the size of the view that satisfies the constraints it holds.
Maybe you created constraints that can be interpreted in the way the size of the cell is equal to (0,0).
There is another way you can check the height of the cell with the text. You can put your text in the UITextView and then:
UITextView textView;
textView.text = #"l";
textView.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE];
//some code here
//
CGFloat cellHeight = textView.contentSize.height;
It is important to set the text and font (and every other property that can cause the change of the height of the UITextView) of the UITextView before using contentSize property. Also you must first add UITextView to the view.
////// EDIT
The problem with your approach with using constraints can be that you want to measure the cell which ISN'T added to the view so the system don't have all the informations it needs. It doesn't know how much space that will be for the space etc because it doesn't know the surrounding area of the cell
I'm writing a simple IRC client that I'm modeling after Twitter's iOS app appearance. I've taken a screenshot of the Twitter app, for reference:
It looks like a simple table view with a few labels inside of each cell. So, in my app, I am programmatically creating a table and the cell formatting. My custom cell has only two labels in it, which I have positioned one on top of the other. The top label is a simple 1-liner. The bottom label I would like to contain longer messages, and need it to word-wrap to multiple lines while staying within my specified width.
How do I achieve this?
So far, I've tried explicitly setting the frame of the label to the dimensions that I want, but it does not word-wrap, if this is all I do. It just flows out of the cell horizontally. I then tried calling sizeToFit, within the tableView:cellForRowAtIndexPath: function, for this label, but it appears to word-wrap at a very small width - the text wraps after like two or three letters and then flows out of the cell vertically.
I can't seem to figure out how to get the text within the label to wrap after a specified width. Any ideas?
My custom cell class: https://github.com/ryancole/pound-client/blob/master/pound-client/views/MessageListCell.m
The cellForRowAtIndexPath function: https://github.com/ryancole/pound-client/blob/master/pound-client/controllers/MessageListViewController.m#L62-L84
Edit 1:
To demonstrate what happened when I set numberOfLines to 0, for unlimited, I have attached a screenshots of that being called. It wraps after a few characters, instead of first taking up the specified width of the UILabel's frame. This is being set prior to called sizeToFit.
You need to set numberOfLines to the number of lines you want, or 0 which allows for an unlimited number of lines (the default is 1). You might also need to set the lineBreakMode to NSLineBreakByWordWrapping (although that might be the default).
After Edit: If you want the text to start at the top, then I think you'll have to use variable height cells, and not set an explicit size for your custom cell. I did it this way in one of my projects:
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = _objects[indexPath.row][#"detail2"];
CGSize labelSize = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(300, 300000) lineBreakMode:NSLineBreakByWordWrapping];
CGFloat h = labelSize.height;
return h + 50;
}
The label I create here, is just for calculating the height of the row, it's discarded after this method ends. The width of the cell is determined by the 300 argument I have in the constrainedToSize: parameter. The +50 was just a fudge factor I added to get my cells looking right -- you'd probably want to mess with that number to get what you want. In my custom cell class, I used initWithStyle:reuseIdentifier, and didn't set any size.