iOS - UITextView doesn't draw text? - iphone

I have a UITextView on the screen.
This UITextView is covered by a UIView.
When I set the text for this UITextView, if the textView is not visible (UIView is covering it), it doesn't draw the text.
So when I remove the view that is covering my textView there is no text in my textView. If I try scrolling the textview the text will appear suddenly.
any way to force the UITextView to draw the text at all times? (This only happens when I have too much text in it (about 1000 characters))
I tried calling [textView setNeedsDisplay], before and after setting 'text' and it didn't fix the problem

I have the same problem, my UITextView does not draw text until I try scrolling it or its parent. [textView setNeedsDisplay] does not work for me. Finally, I increase textView 1px and the problem's gone.
CGRect frame = textView.frame;
frame.size.height += 1;
textView.frame = frame;
I know this method is ugly but it does work.

Most things in the system will not display when hidden, which would be a waste of resources.
The correct approach is to call setNeedsDisplay: on the UITextView AFTER it has been un-hidden, then it should draw immediatley.

The limit to the number of characters in a UITextView seems to be around relatively low (see the post here).
The poster in the link I posted suggests putting your content into a UIWebView, which can handle a large amount of text.

Same problem. We have a textview that is configured and put into the view while hidden or offscreen. Most likely due to Apple optimizing UITextView so that it doesn't do layout and drawing unless it is onscreen and visible.
Calling setNeedsDisplay doesn't work because of the optimizations. setNeedsDisplay gets called quite often, so imagine a UITextView doing layout and drawing quite often? Thus the solution above is ugly but works: add a pixel to the frame which will trigger the UITextView to re-layout and redraw. We went with this solution.

Since UITextView is a subclass of UIScrollView, I did something like this:
CGPoint offset = self.textView.contentOffset;
offset.y++;
[self.textView setContentOffset:offset];
offset.y--;
[self.textView setContentOffset:offset];
the same as #Tùng Đỗ without changing the frame... still looks ugly.

Related

How do I get a UIView which displays text to behave like a UILabel when bounds change?

I have a UIView in which I need to draw text in drawRect:
- (void)drawRect:(CGRect)rect {
...
[#"some text" drawAtPoint:somePoint withFont:someFont];
...
}
Because the text requires special formatting, I cannot just use a UILabel.
It looks fine until I rotate the device. Then the size of my custom UIView changes (in the parent view's layoutSubviews method), and the text becomes stretched in one direction and squished in the other.
When I replace my view with a UILabel, the text always looks great, even when the bounds of the view changes.
How can I get my view to exhibit the same behavior as UILabel?
Some things I have looked into, but have yet to have success with:
Set the view's layer's needsDisplayOnBoundsChange to YES.
Set the view's contentStretch to CGRectZero.
Call setNeedsDisplay in my view's layoutSubviews.
Maybe I'm not doing one of these things right. Has anyone else run into this?
Update: As recommended in James Huddleston's answer, I set the contentMode property of the view to UIViewContentModeRedraw, which got me part of the way there. The text now appears correct at the conclusion of the animation. However, at the start of the animation the text gets squished/stretched to fit the end dimensions and then gets unsquished/unstretched over the course of the animation. This is not the case with UILabel.
Try setting the contentMode property of your view to UIViewContentModeRedraw.
This seems to work OK:
self.contentMode = UIViewContentModeRedraw;
self.contentStretch = CGRectMake(1, 1, 0.5, 0.5);
And then ensure that the bottom-right pixel is the background color. To do that, I put one pixel of padding around the contents of the view. It seems like UILabel doesn't have the one pixel border restriction, so it must be doing something different. But as far as I can tell, this has the same effect.
Use a UIWebView
Sounds a bit overkill but it seems the recommended way to get formatted text that's more complicated than a UILabel can cope with.
There is some source code created by Kevin Ballard called FontLabel. (code.google.com)
The good thing about this, it subclasses UILabel and you can use your own Font ;)
//EDIT: ok ok, as told I will update my answer to recommend subclassing UILabel "to get all the UILabel goodness" ^^

UIView transitionWithView: clipping my UITextView

I have a UITextView with scrolling disabled, and I am using the page-curl transition to change the text inside. This is the code I'm using:
myView.text = nextString;
[UIView transitionWithView:myView duration:PAGE_CURL_DURATION options:UIViewAnimationOptionTransitionCurlDown animations:nil completion:nil];
Everything works fine, except if the new text string has more lines than the old one, it will be clipped until the animation is complete. It looks as though the contentSize is changing AFTER the animation. I tried adding myView.contentSize = myView.bounds.size; but that didn't change anything. I also turned off "clip subviews" to no avail.
This is what it looks like immediately after the animation:
a moment later the text is no longer clipped.
Any ideas how to prevent clipping?
The easy way is to do what Bjarne said - use the clipsToBounds property. But watch out - this will make the text expand downwards indefinitely. So you will also need to surround your text field with a container view that DOES clip to bounds to set an upper limit.
If that doesn't do the trick, you will have to do some work to manually expand your text view's bounds and reposition it before animating if the new text is larger.
To get the size of a text, check out UIStringDrawing.h in the UIKit framework. Specifically, I'm thinking of using:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode;
together with UILineBreakModeWordWrap.
another alternative to getting the size may be this function in UILabel:
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines;
and passing in the bounds of the largest possible text view.
For extra credit, you may also want to consider animating the change in your view's bounds for a smoother transition.
This is rather odd. The contentSize should not clip contents, but only determine scrolling behavior. This however is the only thing changing from pre- to post-animation. And unchecking Clip Subviews always worked for me.
A few options:
I assume your UITextView.frame is large enough to contain the largest of both texts initially? If you set it just before the animation kicks in, you might be too late. In such cases tricks like [self performSelector(continueWithStuff:) withObject:object afterDelay:0.01] do wonders, because you give the UI the time needed to effectuate your changes before the animation will determine the initial state.
But the easiest fix by far will be to add a bunch of newlines to every text. Since you're not scrolling anyway this should not be a problem.
Another angle may be to embed your UITextView in a UIView which you then animate. Reading the docs, the view argument should be "The container view that contains the views involved in the transition." Ignoring this fact may be giving your surprising results.
The easiest way to fix you problem is to set the UITextView's property "clipsToBounds" to NO. This will make your text view available to draw outside of its bounds

UITextView inside UIScrollView scroll problem

I'm experiencing something considered a bug in my situation. Probably this is not a bug but a feature ;).
Here's my case:
I load a UIScrollView with my content. A part of my content is loaded asynchrone after the view is already loaded. This works great and without issue.
Some of these controls however are UITextView controls. Once I set the content (text property) of these controls after the viewDidLoad my UIScrollView scrolls down to the last UITextView that was set. I want to prevent this and want the UIScrollView to maintain it's top scrolled position.
In summary:
If I set the Text property in the viewDidLoad method no scroll occurs. If I set the Text property on the UITextView after the viewDidLoad method (because I load the data asynchronous) the UIScrollView will scroll to the last UITextView that was set. I want to prevent this scroll.
How do I achieve this ? I have a feeling this is just a property which is set wrong but I can't seem to find it.
Thanks in advance.
EDIT:
I've tried setting the "scrollEnabled" property to NO before setting the values and to YES after but this didn't have any effect. The ScrollView will still scroll when the text property of the UITextView is set.
I Fixed the issue with a work-around:
I set the scroll view content size to something small, like 320 x 300 or whatever the height of the scrollview frame is, then did my work, then put the content size back to normal.
This prevents the scrolling while the data is loaded and enables it as soon as the loading is finished.
This would not change the scrolling problem but maybe make it "hidden" for the user:
yourTextView.text = #"Eat more bananas!";
yourScrollView.contentOffset = CGPointMake(0.0, 0.0);
Some snippets of your code would help to face the problem more specific.
EDIT:
Or try to add the UITextViews to a UIView, then add the UIView to the UIScrollView. Make sure that the UIScrollView's contentSize is the same as the Size of the UIView.
This worked for me too :p but i had to set the height to something less then 300 (in my case i just used 10).
Basically the idea is to make the text view not part of the visible area of the UIScrollView wile you are changing the text of th UITextView.

UIScrollView doesn't bounce

I have a UIScrollView contained within a custom UIView with a content size larger than the ScrollView's frame.
I am able to drag scroll as I expect, but the thing doesn't give me the rubber banding effect that you get with the UITableView or UIWebView. It just stops when you get to one of the extremes.
I have set bounce = YES, is there something else I'm supposed to do?
I read the docs, and they say I have to implement the delegate. I did that.
They also say I should change the zoom levels, but I don't want the user to actually be able to zoom so I haven't set these.
For anyone that finds this thread later, if you are subclassing UIView and re-setting the UIScrollView's frame on every layoutSubviews, that is the problem - it cancels the bounce:
http://openradar.appspot.com/8045239
You should do something similar to this:
- (void)layoutSubviews;
{
[super layoutSubviews];
CGRect frame = [self calculateScrollViewFrame];
if (!CGRectEqualToRect(frame, self.scrollView.frame))
self.scrollView.frame = frame;
}
I had the same problem, on a UIScrollView that wasn't all filled up (but I still wanted it to bounce). Just setted:
scroll.alwaysBounceVertical/Horizontal = YES;
And it worked as expected
It turns out that keeping the UIScrollView within my custom UIView was causing the trouble.
Once I switched my custom UIView to instead inherit from UIScrollView, then the bouncing started working.
That is interesting... Is there a lot going on while the user scrolls the scroll view? Maybe that could cause the problem. The iPhone can multitask, but not too much. Can I see your entire code having to do with the scroll view?

iPhone UITextView leaves room for 2 lines at the bottom

When I start typing text in a default textView in my viewcontroller, it's not going to the bottom of the textfield. It leaves room for 2 more lines of text and then starts scrolling.
I want it to start scrolling when I start going beyond the last line.
I tried everything, and I don't know what I can do?
Anyone any ideas?
as UITextView is a subclass of UIScrollView, look at UIScrollVIew's properties, like contentInset and others to see if they might be creating a gap between your frame/bounds and the content inside.
Just adding to what mahboudz already said. Here's some sample code that can be adjusted to get what you need:
UIEdgeInsets contentInset = notes.contentInset;
contentInset.bottom = 10.0;
notes.contentInset = contentInset;
Where notes in this example is the UITextView.
In addition to what mahboudz and Aaron said, I can confirm that UITextView does add some contentInset to the bottom, so I put the following code inside textView:shouldChangeTextInRange:replacementText: of UITextViewDelegate
textView.contentInset = UIEdgeInsetsMake(0, 0, 5, 0);
When moving code from a nib to setting it up programmatically, I ran into a similar problem--wherever the insertion point was, it was scrolled up out of view. I eventually found that my UITextView had a bottom inset of 32 pixels when I created it programmatically, though I'm not sure why. So I fixed it by setting the contentInset when I create the text view:
textView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);