0
votes

I'm trying to scale editable attributed text in a UITextView and adjust the textView's width dynamically. I have no problem scaling the text with a UITextView inside a UIScrollView but I cannot work out what to change in order to retain the textView's width.

enter image description here

The above text is what I start with. Then I scale it and it is cropped to the UIScrollView's bounds.

enter image description here

But what I would really like is to be able to retain the original width and reflow the text as in this image.

enter image description here

I would really appreciate any help on this. I am using autolayout, iOS 7 and xcode 5.

I have tried scaling the fonts inside the string but there is a buffering bug in iOS7 which means the UIGestureRecognizerStateChanged notifications are buffered and the scaling continues long after the pinch gesture has ended for attributed strings with more than just a few font changes. For those interested this is the botch I have at the moment which does scale the fonts but not elegantly at all.

- (void)scaleTextView:(UIPinchGestureRecognizer *)pinchGestureRecognizer
{
    CGFloat scale = 0;
    NSMutableAttributedString *string;

    switch (pinchGestureRecognizer.state) {
        case UIGestureRecognizerStateBegan:
            self.old_scale = 1.0;
            self.last_time = [NSDate date];
            break;

        case UIGestureRecognizerStateChanged:
            scale = pinchGestureRecognizer.scale - self.old_scale;

            if( [self.last_time timeIntervalSinceNow] < -0.2 )  {       //  updating 5 times a second is best I can do - faster than this and we get buffered changes going on for ages!
                self.last_time = [NSDate date];
                string = [self getScaledStringFrom:[self.textview.attributedText mutableCopy] withScale:1.0 + scale];
                if( string )    {
                    self.textview.attributedText = string;
                    self.old_scale = pinchGestureRecognizer.scale;
                }
            }
            break;

        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateFailed:
            break;

        default:
            break;
    }
}

- (NSMutableAttributedString*) getScaledStringFrom:(NSMutableAttributedString*)string withScale:(CGFloat)scale
{
    [string beginEditing];
    [string enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, string.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
        if (value) {
            UIFont *oldFont = (UIFont *)value;
            UIFont *newFont = [oldFont fontWithSize:oldFont.pointSize * scale];
            [string removeAttribute:NSFontAttributeName range:range];
            [string addAttribute:NSFontAttributeName value:newFont range:range];
        }
    }];
    [string endEditing];
    return string;
}

What I am looking for is the frame I need to change in the ScrollView, the TextView, the TextContainer, the LayoutManager or whatever which will cause my string to reflow within an adjusted border when the TextView within has been scaled/zoomed. Alternatively, if someone can correct or improve my code so that it is more performant I'd be extremely grateful.

1
How do you scale? Could it be interesting to change the size font of your NSAttributedString?Larme
Hi @Larme, I scale using pinch gesture in UIScrollView. I did try it with scaling the font but as the complexity of the attributed string grows this would probably become untenable.amergin
Do you also have the issue with "normal" text (not attributed one)? I'd still go with changing the font size. From other discussion, it's the technic used: stackoverflow.com/questions/13669457/…Larme
No, that's the thing, my code before worked fine when I just used non-attributed text because I could scale the font. The problem now is that with any complexity the scaling slows down, this is why I was trying to use a transform on the actual text view so all of its fonts would be scaled without having to traverse it replacing every font.amergin

1 Answers

0
votes

How about using textContainerInset ?

So something like (assuming width is textview original width)

CGFloat rightInset = width * (pinchGestureRecognizer.scale - 1.);
self.textview.textContainerInset = UIEdgeInsetsMake(0, 0, 0, rightInset);

(I did not test this, let me know if it works)