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
Related
I've got a UIView with a UILabel on top. I have the UILabel's content mode set to 'UIContentModeLeft'. As expected, when I animate the frame of the UIView to be smaller than the original size, the label 'jumps' to the final frame without animating nicely.
As far as I can see, UIContentModeRedraw does not force 'drawRect' to be called on every 'animated frame'. I've tried using a custom CALayer as well but can't seem to cause the frame to resize smoothly.
Is there any way to do this? The animation as it stands is extremely glitchy. UIContentModes are not useful and I can't use a frame for contentStretch as well as none of the edges of the label can be stretched. What I really need is a 'refresh' of the label every time the parent view resizes.
There's no way of doing this unfortunately, so I've learnt. The only other way is to actually run a timer that updates the frame every time it's invoked. This results in a very jerky animation given a resize of a complex view is time consuming. I ended up achieving the same thing with some pre-rendered onscreen elements and a whole lotta 'magical effects' behind the scene.
I am working in an app in which I need to give feature like Notes App in iphone. as shown in first screen shot , initially , notes leaves a tab before the content starts, I also wanted to do the same and for that when I set Left Content inset (of UITextView) by 25 , it shows like in screenshot 2, here you may see the image also gets shifted. I have set image as background. I don't know how to solve this problem.
I also tried by adding image as subview of UITextview but it won't repeat the lines, while scrolling (image of lines) like notes app.
I'm setting the background of Textview by following code.
[textView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"line_image.png"]]];
Please tell me if I am going wrong or any extra effort needed to get desired output.
Thanks
UITextView is UIScrollView subclass so all relevant delegate method are available for you (e.g. scrollViewDidScroll:) - you can adjust your custom background in that method.
There's very nice post on Dr.Touch blog about recreating Notes app interface - you can get general idea about how it is done from it. Basically what is done there is adding custom view that draws background behind the text view and adjust it in text view's delegate methods and also using KVO on its 'contentSize' property.
#Dinesh gave nice solution but it doesn't sound to be generic as I want to use image of lines instead of drawing them. Image has some special effects that can not be achieved by drawing. So to solve it I created a table view below the textview keeping textview's background transparent. Now just added the image of line to my tableview's custom cell and set content offset of UItableview same as of the scrollview (subview of text view ,refering to the answer of #Vladimir ) .
And used the following code to scroll my tableview with scrolling the textview and got the desired output.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
tableView.contentOffset =scrollView.contentOffset;
}
Keeping my tableview's number of rows at a very large number.
PS: instead of setting the content inset of textview, i set its frame's X position and decreased the width relaively.
I have an iOS app which boils down to the following:
A view controller which implements all the UIScrollViewDelegate methods.
A customised UIImageView which is the view returned by viewForZoomingInScrollView in the first view controller.
In the drawRect method of the customised UIImageView I overlay some text at various positions on the image, the text can be at various sizes and angles, constrained by a rectangle defined by points on the image. So the co-ordinate system is all relative to the image associated with the UIImageView.
Sometimes the text is having to be rendered into areas that are far too small to allow the text to be readable at the default zoom level.
When I zoom in, however, this doesn't make the text any more readable as the text was rendered on the original UIImageView at the font size appropriate for the bounds it was given.
What I'd like is some way to redraw this text, in the correct location still, at a proportionally larger font size when zoomed in. Is this possible, and how would I do it?
I've been working on a zoomable floorplan myself and dealt with it in a rather simple way.
I put my text in labels and put those labels on top of the ImageView.
While zooming the labels scale with the UIImageView and thus the text within scale as well.
When zooming in too much however text becomes vague and blurry so you might want to update those text labels in the:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
For me this was sufficient, I hope this might be so for you as well.
Instead of trying to fix this, I'd say your design is a bit wrong. I'd reengineer this so that you're rendering the entire contents that you're displaying in the highest zoom that you want, and then allow the scrollview to zoom in/out of the finished content by specifying the zoom parameters. Otherwise you'll go insane trying to fix text. Just draw the entire image / document as big as you want it to be, then add THOSE contents to the scroll view, at some lower zoom, allowing the user to zoom in/out as they desire. Then you're done.
You can write your overlay text on UILabels, attached to the UIViewController view.
Then you need to make your own custom class from UIScrollView and override
- (void)setContentOffset:(CGPoint)anOffset
here, using the anOffset and self.zoomScale you can place your labels at the places you want, with apropriate font size and all. So, they will change their positions every time your scrollview moves or zooms.
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" ^^
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.