Reputation: 768
I have a UITextView in my app. I need to change it's content offset dynamically every time a new string is appended. The code bellow works fine on iOS 6 and earlier versions, but not on iOS 7.
CGPoint offset = CGPointMake(0, self.textView.contentSize.height - self.textView.frame.size.height);
[self.textView setContentOffset:bottomOffset animated:YES];
Do you have any ideas why?
Than you.
UPDATE
The UITextView doesn't set its contentOffset as it should, or something else mess it up. The result is not as expected (how it is on iOS 6 or earlier versions).
I've also tried setting the new property "textContainerInset" but still no results...
self.textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
UPDATE
As I can see in the didScroll delegate method, when I pass a positive value for content offset, it scrolls to (0,0). For example, I set the content offset to (0,35) and it scrolls to (0,0)...
Upvotes: 16
Views: 15520
Reputation: 768
The solution I've found is not elegant at all, but it works:
I set the UITextView's contentOffset to (0,0) before setting it to the desired offset. I don't know why this works, but for now is the best solution I've found.
CGPoint bottomOffset = CGPointMake(0, self.textView.contentSize.height - self.textView.frame.size.height);
[self.textView setContentOffset: CGPointMake(0,0) animated:NO];
[self.textView setContentOffset:bottomOffset animated:YES];
Upvotes: 8
Reputation: 768
Ok, based on my research and other answers, the problem was the UITextView
's content height and not the scrolling to a specific offset. Here is a solution that works the way it should work for iOS 7:
First, you need to recreate the UITextView
like this:
NSString *reqSysVer = @"7.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
if (osVersionSupported) {
NSLog(@"reset chatoutput");
CGRect outputFrame = self.chatOutput.frame;
[chatOutput removeFromSuperview];
[chatOutput release];
chatOutput = nil;
NSTextStorage* textStorage = [[NSTextStorage alloc] init];
NSLayoutManager* layoutManager = [NSLayoutManager new];
[textStorage addLayoutManager:layoutManager];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.view.bounds.size];
[layoutManager addTextContainer:textContainer];
chatOutput = [[UITextView alloc] initWithFrame: outputFrame
textContainer: textContainer];
// if using ARC, remove these 3 lines
[textContainer release];
[layoutManager release];
[textStorage release];
[self.view addSubview: chatOutput];
}
Then, use this method to get the UITextView
content height:
- (CGFloat)textViewHeightForAttributedText:(NSAttributedString*)text andWidth:(CGFloat)width
{
UITextView *calculationView = [[UITextView alloc] init];
[calculationView setAttributedText:text];
CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
NSLog(@"size: %f", size.height) ;
return size.height;
}
Now you can set the content offset:
CGPoint bottomOffset;
bottomOffset = CGPointMake(0, [self textViewHeightForAttributedText: self.chatOutput.attributedText andWidth: self.chatOutput.frame.size.width] - self.chatOutput.frame.size.height);
[self.chatOutput setContentOffset:bottomOffset animated:YES];
UPDATE
I've read this in the Apple Documentation for NSAttributedString
:
"The default font for NSAttributedString objects is Helvetica 12-point, which may differ from the default system font for the platform."
In conclusion, if you use different fonts of different sizes, you have to set those to the NSAttributeString
instance as well. Otherwise, the returned height won't correspond to your expectations. You may want to use something like this:
NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObject: [UIFont systemFontOfSize: 18.0] //or any other font or size
forKey: NSFontAttributeName];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString: currentPost.postMessageText attributes: attrsDictionary];
frame.size.height = [self textViewHeightForAttributedText: attributedString andWidth: 280.0];
[attributedString release];
Upvotes: 4
Reputation: 410
None of previous answers worked for me, because I had [textView setScrollEnabled:NO]
. So, I ended up with this:
[textView setScrollEnabled:YES]
[self.text setContentOffset:CGPointMake(0, page * self.text.frame.size.height) animated:NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.text setScrollEnabled:NO];
});
Upvotes: 0
Reputation: 1377
@vidonism's answer works but this is even better.
// in -viewDidLoad of UIViewController
self.automaticallyAdjustsScrollViewInsets = NO;
See also top comment of accepted answer on this question. This likely explains the behavior of a UIScrollView being the first subView of a ViewController's main view.
I can't quite discren if this is expected behavior or a bug.
Upvotes: 2
Reputation: 119
I just noticed that today. In my case the issue is apparently related to the Text View being the first control in the view. I moved it to second place in the document outline (after a UILabel for example) and the offset is gone.
Most probably this is done on purpose because of the navigation bar new behaviour in iOS7.
Upvotes: 5
Reputation: 247
I've had to resort to
[displayText scrollRangeToVisible:NSMakeRange([displayText.text length], 0)];
but the BIG problem with it is that it starts from the top of the text and scrolls to the end each time you append to the text. Mihai's solution also does that.
Upvotes: 0