Reputation: 8918
Summary:
In iOS 6, I centered text vertically in a UITextView by key-value-observing the text view's contentSize property (https://stackoverflow.com/a/12591299/1239263). When I upgraded to iOS 7, the latter technique worked inconsistently.
Rather than try to fix the KVO technique, I would rather use Text Kit to center text vertically in a UITextView.
I have designed a solution using Text Kit, but it breaks upon device rotation.
The UITextView is orange.
When the text view initially loads, the text is centered properly:
When the device rotates to landscape, the text is still centered properly:
However, when the device rotates back to portrait, the text is not centered properly. The text should be on one line, as it was in the first screenshot above.
For the latter screenshot, logging geometries to the console reveals that the text view's text-container width is too narrow.
Details:
@interface ViewController ()
// text view is created in storyboard; it's added to the root view and it's using auto layout
@property (weak, nonatomic) IBOutlet UITextView *textView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSTextContainer *textContainer = self.textView.textContainer;
[textContainer setWidthTracksTextView:YES];
}
- (void)centerText
{
NSTextContainer *container = self.textView.textContainer;
NSLayoutManager *layoutManager = container.layoutManager;
CGRect textRect = [layoutManager usedRectForTextContainer:container];
UIEdgeInsets inset = UIEdgeInsetsZero;
inset.top = self.textView.bounds.size.height / 2 - textRect.size.height / 2;
inset.left = self.textView.bounds.size.width / 2 - textRect.size.width / 2;
self.textView.textContainerInset = inset;
}
- (void)viewDidLayoutSubviews
{
[self centerText];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self centerText];
}
What I have tried:
I tried to manually set the container size in the centerText method. Depending upon what value I set for the container size, setting the container size does actually work in some cases. Which cases? It depends upon the number of lines of text displayed.
// fixes problem if a single line of text; clips multi-line text
container.size = CGSizeZero;
// fixes problem if multi-line text, but not if a single line of text
container.size = CGSizeMake(self.textView.bounds.size.width, FLT_MAX);
Since I'm setting widthTracksTextView to YES, I don't understand why I would need to set the text container size at all. And if I do need to set the text container width, why does the correct value seem to depend upon the number of lines displayed?
Upvotes: 6
Views: 3094
Reputation: 71
I think I found both explanation and solution. The container itself has some problems with fragment of lines. This is what I saw in the package.
Returns the bounds of a line fragment rect inside the receiver for proposedRect. This is the intersection of proposedRect and the receiver's bounding rect defined by -size property. The regions defined by -exclusionPaths property are excluded from the return value. charIndex is the character location inside the text storage for the line fragment being processed. It is possible that proposedRect can be divided into multiple line fragments due to exclusion paths.
When you use rotation of screen, the exclusionPaths are excluded. So the text cannot save the orientation value you want. That is why when you define the CGRect, it solved the problem somehow. So the method you should use is:
- (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect atIndex:(NSUInteger)characterIndex writingDirection:(NSWritingDirection)baseWritingDirection remainingRect:(CGRect *)remainingRect;
Upvotes: 0