7
votes

I have a UITextView, and I am trying to animate a change of frame when the user taps on a button. Basically the text view grows larger to fit the screen so it can display more text, and then when the user taps the button again it shrinks to its original frame.

I perform the animation using blocks, like this:

if(!isDisplayingDetailView)
{
    //Expand view
    [UIView animateWithDuration:0.5
                     animations:^{
                         self.detailView.frame = self.view.frame;
                     }
                     completion:^(BOOL finished){
                         isDisplayingDetailView = YES;
                     }];
}
else{
    //Shrink view.
    [UIView animateWithDuration:0.5
                     animations:^{
                         self.detailView.frame = self.detailFrame;
                         }
                     completion:^(BOOL finished){
                         isDisplayingDetailView = NO;
                     }];
}

Where self.detailView is the UITextView, and self.detailFrame is just a CGRect holding the original frame. isDisplayingDetailView is just a BOOL to check if the view is expanded or not. My problem is that the text resize is not animating. I made some screenshots from a test app to illustrate my problem: App default view: Default view

The expanded view:

Expanded view

The textview just a moment after tapping the button:

Shrinking view

As you can see the text automatically shrinks to the final frame size, without any kind of animation, while the bounds still are animating. I guess that's the right behavior, but I'd like to know if it's possible to animate the text along with its view.

2
Hi, Did u find solution to this, if yes please share with me, I am also stuck in same problemVinayaka Karjigi
@VinayakaKarjigi sorry but I found no solution.The dude
had the same problem. my solution was to render an image of the textview put it where the textview was, hide the textview, and then animate the image.peko

2 Answers

2
votes

The text in textviews doesn't always act as expected. For this you'll have to set up a NSTimer and set the frame size on every tick.

Do something like:

textview.frame = CGRectMake (textview.frame.origin.x, textview.frame.origin.y, textview.frame.size.width-1, textview.frame.size.height-1);

Then when it's done I would completely remove the textview from superview and set it to nil, and then init a new one. The reason for this is that when changes the frames of textviews for some reason padding is added around the text's box. You won't notice it unless you change the frame a probably at least 100 times.

[textview removeFromSuperview];
textview = nil;
textview = [[UITextView alloc] initWithFrame:CGRectMake(x, y, width, height)];
textview.text = yourTextString;
//You will also have to set whatever non default properties you want, such as text or background color
[view addSubview:textview];
1
votes

Another solution is to animate the bounds change.

There are a few ways, but here's a simple subclass of UITextView which does exactly this.

import Foundation
import UIKit

import QuartzCore

class SmoothTextView : UITextView {

    override func actionForLayer(layer: CALayer!, forKey key: String!) -> CAAction! {

        if key == "bounds" {

            let x = super.actionForLayer(layer, forKey: "backgroundColor")

            if let action:CAAnimation = x as? CAAnimation {

                let transition = CATransition()

                transition.type             = kCATransitionFade
                transition.beginTime        = action.beginTime
                transition.duration         = action.duration
                transition.speed            = action.speed
                transition.timeOffset       = action.timeOffset
                transition.repeatCount      = action.repeatCount
                transition.repeatDuration   = action.repeatDuration
                transition.autoreverses     = action.autoreverses
                transition.fillMode         = action.fillMode

                transition.timingFunction = action.timingFunction
                transition.delegate = action.delegate

                return transition
            }
        }

        return super.actionForLayer(layer, forKey: key)
    }
}

This as of iOS 8.

As an extra tweak, you might want to configure the text of your text view instance by adjusting the text container by zeroing all padding or insets:

textView.textContainer.lineFragmentPadding = 0.0
textView.textContainerInset = UIEdgeInsetsZero