I have a UITableViewController, and I'd like to make it not flash the vertical scroll bar when I go back from a push action segue on one of it's cells (popping the view controller and going back to the UITableViewController).
It seems that, if the table has many rows (mine has around 20 with 60 points height each, so bigger than the screen), when I go back, it always flashes the vertical scroll bar once to show where it is in the table. However, I don't want that to happen, but I do want to keep the scrollbar around so it shows when the user scrolls. Therefore, disabling it completely is not an option.
Is this default behavior and can I disable it temporarily?
There is a simpler solution that doesn't require avoiding using a UITableViewController subclass.
You can override viewDidAppear: as stated by http://stackoverflow.com/users/2445863/yonosoytu, but there is no need to refrain from calling [super viewDidAppear:animated]. Simply disable the vertical scrolling indicator before doing so, and then enable it back afterwards.
- (void)viewDidAppear:(BOOL)animated {
self.tableView.showsVerticalScrollIndicator = NO;
[super viewDidAppear:animated];
self.tableView.showsVerticalScrollIndicator = YES;
}
If you're using Interface Builder, you can disable the Shows Vertical Indicator option on the tableView for your UIViewController and enable it in code as shown above.
To get Cezar's answer to work for iOS10 I had to include a (sizeable) delay before re-enabling the scroll indicator. This looks a bit strange if someone tries to scroll before the second is up, so you can re-enable the scroll indicator as soon as someone scrolls.
override func viewDidAppear(_ animated: Bool) {
tableView.showsVerticalScrollIndicator = false
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.tableView.showsVerticalScrollIndicator = true
}
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !tableView.showsVerticalScrollIndicator {
tableView.showsVerticalScrollIndicator = true
}
}
Actually, on thinking about it, you don't even need the delay, just do this:
override func viewDidAppear(_ animated: Bool) {
tableView.showsVerticalScrollIndicator = false
super.viewDidAppear(animated)
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !tableView.showsVerticalScrollIndicator {
tableView.showsVerticalScrollIndicator = true
}
}
Update: Please, look at Cezar’s answer below, which gives a nice workaround without any of the drawbacks of my proposals.
According to the documentation it is a behaviour of UITableViewController:
When the table view has appeared, the controller flashes the table view’s scroll indicators. The UITableViewController class implements this in the superclass method viewDidAppear:.
So I think you have two options:
You can avoid using UITableViewController and start using a naked UIViewController. Rebuilding the functionality of UITableViewController from UIViewController is not that hard (you can follow this old article as reference).
Override viewDidAppear: and don’t call [super viewDidAppear:animated]. The problem here is that you don’t know what else does UITableViewController do when viewDidAppear: is called, so you might break something.
Related
I have a Text View (UITextView) which displays a long text that is set on runtime like so:
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
if something {
textView.text = "(very long text here)"
}
textView.contentOffset = CGPoint.zero // doesn't work
}
Unfortunately, when the Text View is displayed, the text is not scrolled to the top but somewhere in the middle.
I'm thinking, either setting the contentOffset is the wrong way of doing it or I am doing it at the wrong time (maybe the text gets changed after setting contentOffset?).
I have tried a lot, I even contacted Apple Code Level Support. They couldn't help me, really (which surprised the hell out of me) – can you?
I'd very much appreciate it. Thank you.
I had a very similar issue, especially when using splitview and testing on the iPhoneX, I resolved this by incorporating this bit of code in my ViewController when I needed the textView to scroll to the top:
textView.setContentOffset(.zero, animated: false)
textView.layoutIfNeeded()
If you wish to scroll to the top of the textView upon loading your ViewController:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Can add an if statement HERE to limit when you wish to scroll to top
textView.setContentOffset(.zero, animated: false)
}
You may need to write more information/code because if you try this piece of code in clean project with only one VC with UITextView, you'll see that it's actually working. If you're really using some condition (if something) this might be the issue. What is this something in your real code?
You need to use viewDidLayoutSubviews() so that it set the scrollview to the top.
override func viewDidLayoutSubviews() {
textView.setContentOffset(.zero, animated: false)
}
I have what I believe to be a standard UITextView in a ViewController which has a substantial amount of text in it, enough that not all of it can fit on the screen. What I would like to happen is that when that view is loaded, the user can start reading at the top of the text and then scroll down the page as they progress through the text. Makes sense, right? What I want is not unrealistic.
The problem is that when the view loads, the text in the UITextView is already scrolled all the way down to the bottom. I have scoured SO and there are a number of similar posts but none of the solutions therein are resolving my problem. Here is the code in he view controller:
import UIKit
class WelcomeTextVC: UIViewController {
var textString: String = ""
#IBOutlet weak var welcomeText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.translucent = false
self.welcomeText.text = textString
self.welcomeText.scrollRangeToVisible(NSMakeRange(0, 0))
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
self.welcomeText.scrollRangeToVisible(NSMakeRange(0, 0))
welcomeText.setContentOffset(CGPointMake(0, -self.welcomeText.contentInset.top), animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I have tried most of the standard solutions to no avail. The one common suggestion which I have not tried is to "Uncheck the Adjust Scroll View Insets in the Attributes Inspector". The reason I have not tried it is because I cannot locate this fabled check box.
What do I need to do to make the text start out aligned to the top?
There's a couple ways I know of. Both ways are implemented programmatically through the viewDidLayoutSubviews() method in your view controller. After the call to super.viewDidLayoutSubviews(), you could add:
myTextView.scrollRangeToVisible(NSMakeRange(0, 1))
This would automatically scroll the textView to the first character in the textView. That however might add some unwanted animation when the view appears. The second way would be by adding:
myTextView.setContentOffset(CGPoint.zero, animated: false)
This scrolls the UITextView to point zero (the beginning) and gives you control over whether you want it animated or not.
A better Solution is to call the textView.setContentOffset(.zero, animated: false) inside the viewWillAppear(_ animated: Bool) lifecycle method instead of in the viewDidLayoutSubviews() method. Just override the default implementation in your custom UIViewController subclass:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textView.setContentOffset(.zero, animated: false)
}
This will scroll to the top of the UITextView only when the view appears and not always when the layout changes (which happens more often as you might think).
If you only want that the UITextView scrolls to the top once and not every time the view appears, you can add a flag. This is really helpful if your UITextView is inside a UINavigationController and the user can push another UIViewController on top of it. After the user returns to the UITextView it keeps the scroll position of the UITextField and does not reset the position to the top:
private var didAppearOnce = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !didAppearOnce {
didAppearOnce = true
textView.setContentOffset(.zero, animated: false)
}
}
When I have text that does not fill the UITextView, it is scrolled to the top working as intended. When there is more text than will fit on screen, the UITextView is scrolled to the middle of the text, rather than the top.
Here are some potentially relevant details:
In viewDidLoad to give some padding on top and bottom of UITextView:
self.mainTextView.textContainerInset = UIEdgeInsetsMake(90, 0, 70, 0);
The UITextView uses auto layout to anchor it 20px from top, bottom and each side of the screen (done in IB) to allow for different screen sizes and orientations.
I can still scroll it with my finger once its loaded.
EDIT
I found that removing the auto layout constraints and then fixing the width only seems to fix the issue, but only for that screen width.
add the following function to your view controller class...
Swift 3
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(.zero, animated: false)
}
Swift 2.1
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(CGPointZero, animated: false)
}
Objective C
- (void)viewDidLayoutSubviews {
[self.mainTextView setContentOffset:CGPointZero animated:NO];
}
UITextView is a subclass of UIScrollView, so you can use its methods. If all you want to do is ensure that it's scrolled to the top, then wherever the text is added try:
[self.mainTextView setContentOffset:CGPointZero animated:NO];
EDIT: AutoLayout with any kind of scrollview gets wonky fast. That setting a fixed width solves it isn't surprising. If it doesn't work in -viewDidLayoutSubviews then that is odd. Setting a layout constraint manually may work. First create the constraints in IB:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewWidthConstraint;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeightConstraint;
then in the ViewController
-(void)updateViewConstraints {
self.textViewWidthConstraint.constant = self.view.frame.size.width - 40.0f;
self.textViewHeightConstraint.constant = self.view.frame.size.height - 40.0f;
[super updateViewConstraints];
}
May still be necessary to setContentOffset in -viewDidLayoutSubviews.
(Another method would be to create a layout constraint for "'equal' widths" and "'equal' heights" between the textView and its superView, with a constant of "-40". It's only 'equal' if the constant is zero, otherwise it adjusts by the constant. But because you can only add this constraint to a view that constraints both views, you can't do this in IB.)
You may ask yourself, if I have to do this, what's the point of AutoLayout? I've studied AutoLayout in depth, and that is an excellent question.
Swift
self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
Objective-C
[self.textView scrollRangeToVisible:(NSMakeRange(0, 0))];
i had same issue! Reset to suggested constrains and just put (y offset)
#IBOutlet weak var textContent: UITextView!
override func viewDidLoad() {
textContent.scrollsToTop = true
var contentHeight = textContent.contentSize.height
var offSet = textContent.contentOffset.x
var contentOffset = contentHeight - offSet
textContent.contentOffset = CGPointMake(0, -contentOffset)
}
For iOS9 and later the textview even on viewWillAppear: is coming with CGRect(0,0,1000,1000). In order for this to work you have to call in viewWillAppear:
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
// * Your code here
After that the textview will have correct CGRect data and you can perform any scrolling operation you may need.
The problem with putting code in viewDidLayoutSubviews and viewWillLayoutSubviews is that these methods are called a lot (during device rotation, resizing views etc ...). If you're reading something from text view, and you rotate the device, you expect that the part of the content you're viewing stays on screen. You do not expect that it scrolls back to top.
Instead of scrolling the content to top, try to keep text view's scrollEnabled property set to NO (false), and turn it back on in viewDidAppear.
If you don't wanna mess with constraints:
override func updateViewConstraints() {
super.updateViewConstraints()
}
override func viewDidLayoutSubviews() {
self.textLabel.setContentOffset(CGPointZero, animated: false)
}
This is an interesting bug. In our project, this is only occurring on devices with an iPhone 5-size screen. It appears that the textview contentOffset changes at some point during the view controller lifecycle. In viewDidLoad and viewWillAppear the textview's contentOffset is 0,0, and by viewDidAppear it's changed. You can see it happening in viewWillLayoutSubviews. Constraints appear to be set up correctly.
This will ensure you don't call a scrolling method unless it's needed:
if textView.contentOffset.y > 0 {
textView.contentOffset = CGPoint(x: 0, y: 0)
// Or use scrollRectToVisible, scrollRangeToVisible, etc.
}
Swift
override func viewDidLoad() {
super.viewDidLoad()
textView.isScrollEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.isScrollEnabled = true
}
For me this works in a different way, I tried all things mentioned above but none of the worked in func viewWillAppear(_ animated: Bool). Which eventually makes textView scrolled up, and in func viewDidAppear(_ animated: Bool) it would scroll after screen appeared.
Below worked for me but got some constraint related issue with keyboard up and down.
override func viewDidLayoutSubviews() {
self.textView.setContentOffset(.zero, animated: false)
}
Below worked as expectation:
override func viewDidLoad() {
super.viewDidLoad()
self.textView.scrollsToTop = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
self.textView.setContentOffset(.zero, animated: false)
}
David Rectors answer in Objective C:
#import "TopTextView.h"
#implementation TopTextView
bool scrolled = NO;
- (void) layoutSubviews
{
[super layoutSubviews];
if (!scrolled) {
[self setContentOffset:CGPointMake(0, 0) animated:NO];
scrolled = YES;
}
}
#end
It seems like a terrible idea to handle this issue in code in the view controller because: A. The view controller isn't making any mistake or doing anything wrong, and B, if you have more than one view controller with a wrongly scrolled text view, you end up with redundant code. The solution should be to write code that exists in the text view class. My solution works with Interface Builder where I simply select a custom class for the UITextView and use this class:
import Foundation
import UIKit
class TopTextView: UITextView {
var scrolled = false
override func layoutSubviews() {
super.layoutSubviews()
if scrolled { return }
setContentOffset(.zero, animated: false)
scrolled = true
}
}
This worked for me. I happen to have a view controller with a child view with a UITextView as a child of that view, not with a UITextView as the child of the view controller. I don't know how well this works if the text view is under top or bottom bars but since no edge insets are touched, this should work.
In my case I had to do it like this:
textView.setContentOffset(CGPoint(x: 0, y: -self.textView.adjustedContentInset.top), animated: false)
because the texview was underneath the navigation bar and had an adjusted inset
I'll get right to it. I have a UItextView placed in my view that when needs to scroll to see all the text (when a lot of text is present in the textView) the textView starts in the middle of the text sometimes and the bottom of the text other times.
Editing is not enabled on the textView. I need a way to force the textView to start at the top, every time. I saw some questions somewhat like this where other people used a content offset, but I do not really know how that works or if it would even be applicable here.
Thanks for your help.
That did the trick for me!
Objective C:
[self.textView scrollRangeToVisible:NSMakeRange(0, 0)];
Swift:
self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
Swift 2 (Alternate Solution)
Add this override method to your ViewController
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
textView.setContentOffset(CGPointZero, animated: false)
}
Swift 3 & 4 (syntax edit)
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
textView.contentOffset = .zero
}
All of the answers above did not work for me. However, the secret turns out to be to implement your solution within an override of viewDidLayoutSubviews, as in:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
welcomeText.contentOffset = .zero
}
HTH :)
In Swift 2
You can use this to make the textView start from the top:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myTextView.setContentOffset(CGPointZero, animated: false)
}
Confirmed working in Xcode 7.2 with Swift 2
Try this below code -
if ( [self respondsToSelector:#selector(setAutomaticallyAdjustsScrollViewInsets:)]){
self.automaticallyAdjustsScrollViewInsets = NO;
}
Or you can also set this property by StoryBoard -
Select ViewController then select attributes inspector now unchecked Adjust Scroll View Insets.
For Swift >2.2, I had issues with iOS 8 and iOS 9 using above methods as there are no single answer that works so here is what I did to make it work for both.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if #available(iOS 9.0, *) {
textView.scrollEnabled = false
}
self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 9.0, *) {
textView.scrollEnabled = true
}
}
Update your UINavigationBar's translucent property to NO:
self.navigationController.navigationBar.translucent = NO;
This will fix the view from being framed underneath the navigation bar and status bar.
If you have to show and hide the navigation bar, then use below code in your viewDidLoad
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone; // iOS 7 specific
Hope this helps.
Xcode 7.2 7c68; IOS 9.1
My ViewController which contains UITextView is complicated, and changed a lot during the project (IDE version changed maybe 2~3 times too).
I've tried all above solutions, if you encounter the same issue, be PATIENT.
There are three possible 'secret codes' to solve:
textView.scrollEnabled = false
//then set text
textView.scrollEnabled = true
textView.scrollRangeToVisible(NSMakeRange(0, 0))
textView.setContentOffset(CGPointZero, animated: false)
And there are two places you can put those codes in:
viewDidLoad()
viewDidLayoutSubviews()
Combine them, you'll get 3*2=6 solutions, the correct combination depends on how complicated you ViewController is (Believe me, after delete just a view above textView, I need to find a new combination).
And I found that:
When put 'secret codes' in viewDidLayoutSubviews(), but textView.text = someStrings in viewDidLoad(), the content in textView will 'shake' sometimes. So, put them in the same place.
Last word: try ALL combinations, this is how I solve this stupid bug more than three times during two months.
With a lot of testing, i found must add below in viewWillLayoutSubviews() function to make sure the UITextView show up from the very beginning:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
textViewTerms.scrollRangeToVisible(NSMakeRange(0, 0))
}
UITextView scrolling seems to be a problem to a lot of people. Gathering from the answers here around (especially this) and the Apple Developer documentation, using some of my own wit, here is a solution that works for me. You can modify the code to suit your needs.
My use case is as follows: the same UITextView is used for different purposes, displaying varying content in different circumstances. What I want is that when the content changes, the old scroll position is restored, or at times, scrolled to the end. I don't want too much animation when this is done. Especially I don't want the view to animate like all the text was new. This solution first restores the old scroll position without animation, then scrolls to the end animated, if so desired.
What you need to do (or should I say can do) is extend UITextView as follows:
extension UITextView {
func setText(text: String, storedOffset: CGPoint, scrollToEnd: Bool) {
self.text = text
let delayInSeconds = 0.001
let popTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue(), {
self.setContentOffset(storedOffset, animated: false)
if scrollToEnd && !text.isEmpty {
let popTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue(), {
self.scrollRangeToVisible(NSMakeRange(text.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) - 1, 0))
})
}
})
}
}
What this does is it updates the text, then uses a stored value of the UITextView.contentOffset property (or anything you pass as a parameter), and sets the offset of the view accordingly. If desired, after this, it scrolls to the end of the new, potentially changed content.
I'm new to iOS programming and I don't know why it works so well it does, if someone has some information on this it would be nice to know. Also the approach may not be perfect so I'm open to improvement ideas as well.
And of course thanks to NixonsBack for posting the answer behind the link above.
My first post :), cheers!
Put this one line of code in ViewDidLoad
self.automaticallyAdjustsScrollViewInsets = false
The following code should give you effect you want.
[self.scrollView setContentOffset:CGPointMake(0, -self.scrollView.contentInset.top) animated:YES];
You'll need to replace "self.scrollView" with the name of your scroll view. You should put this code in after you've set the text of the scroll view.
This worked for me:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
textView.scrollRectToVisible(CGRect(origin: CGPointZero, size: CGSizeMake(1.0, 1.0)), animated: false)
}
This worked for me with Xcode 8.3.3:
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[self.txtQuestion scrollRangeToVisible:NSMakeRange(0, 0)];
}
Create an outlet for your UITextView in the ViewController.swift file. In the ViewDidLoad section put the following:
Swift:
self.textView.contentOffset.y = 0
I have tried:
self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
I translated zeeple's answer to MonoTouch/Xamarin (C#).
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
myForm.SetContentOffset(new CoreGraphics.CGPoint(0,0), animated: false);
}
I had to implement two answers here to get my view working as I want:
From Juan David Cruz Serrano:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
textView.setContentOffset(CGPoint.zero, animated: false)
}
And from Murat Yasar:
automaticallyAdjustsScrollViewInsets = false
This gave a UITextView that loads with the scroll at the very top and where the insets are not changed once scrolling starts. Very strange that this is not the default behaviour.
To force the textView to start at the top every time, use the following code:
Swift 4.2:
override func viewDidLayoutSubviews() {
textView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
}
Objective-C:
- (void)viewDidLayoutSubviews {
[self.yourTextView setContentOffset:CGPointZero animated:NO];
}
Swift 4.2 & Swift 5
set content offset both before and after setting the text. (on the main Thread)
let animation = false //or whatever you want
self.mainTextView.setContentOffset(.zero, animated: animation)
self.mainTextView.attributedText = YOUR_ATTRIBUTED_TEXT
self.mainTextView.setContentOffset(.zero, animated: animation)
In my case I was loading a textView in a Custom tableview cell. Below is what I did to make sure the text in a textview loads at the top of the text in my textview in my custom cell.
1.) In storyboard, set the textview ScrollEnabled = false by unchecking the button.
2.) You set the isScrollEnabled to true on the textview after the view loads. I set mine in a small delay like below:
override func awakeFromNib() {
super.awakeFromNib()
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when){
self.textView.isScrollEnabled = true
}
}
Regardless, if you are in my situation or not, try setting scrollEnabled to false and then when the view loads, set scrollEnabled to true.
Here is the problem:
I have a bunch of views that I put in a UIScrollView. The size and position of those subviews are defined by constraints. This works perfectly, and scrolling works too. So far so good.
However, I want my scrollview to scroll all the way to the bottom when I show the viewcontroller on screen for the first time, and this is where trouble starts. In order to know where the bottom is I need to know the position and size of the lowest element in my subviews. Should be easy too (since I have the reference to that UIView somewhere): get the frame of the UIView and voila.
I want to scroll the scrollview to the bottom before it appears on screen (so basically, in viewWillAppear:), but the constraints only get evaluated after viewWillAppear and before viewDidAppear: is called.
Getting the UIView frame in viewWillAppear gives me a zero sized CGRect. Doing the same in viewDidAppear gives me the correct CGRect. But viewDidAppear is too late for me, since the scrollview is on screen already so you see the content moving up.
Does anyone have a good solution for this? I tried putting the code in viewDidLayoutSubviews but that doesn't work either.
The problem with viewWillAppear is that it is called before the layout happens. You have to adjust the scroll view's content offset after it is laid out - in viewDidLayoutSubviews method. in Here is my solution:
#property (nonatomic, assign) BOOL shouldScrollToBottom;
- (void)viewDidLoad
{
[super viewDidLoad];
_shouldScrollToBottom = YES;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// Scroll table view to the last row
if (_shouldScrollToBottom)
{
_shouldScrollToBottom = NO;
[_scrollView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
}
}
I was running into this problem too! The view didn't scroll to the desired page before viewDidAppear(). It had multiple subviews to be layed out by viewDidLayoutSubviews(), so doing it once in that function, like Thomas suggested, didn't work for me either. However, the viewDidLayoutSubviews() approach works, if you use a little trick:
You need to attempt to scroll to the desired point every time viewDidLayoutSubviews() is called during the creation of the view. Then, after the view is displayed, you don't want this to be called anymore. So you add a control variable that is changed in viewDidAppear(), after the initial layout. Here's the full example:
var needsFirstScroll = true
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if(needsFirstScroll) {
var frame = CGRect() // placeholder, do your frame calculation here
scrollView.scrollRectToVisible(frame, animated: false)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
needsFirstScroll = false
}
This is what I did and it works for me (cell height computed by autolayout):
class MessagesTableViewController: UITableViewController
{
var shouldScrollToBottom = false
override func viewDidLoad()
{
super.viewDidLoad()
...
shouldScrollToBottom = true
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
if(shouldScrollToBottom == true)
{
shouldScrollToBottom = false
goToBottom()
}
}
private func goToBottom()
{
if(data.count == 0)
{
return
}
let indexPath = NSIndexPath(forRow: tableView.numberOfRowsInSection(0)-1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: false)
}
}
The solution I finally came up with was to:
Store all subviews of the scrollview in 1 contentview and not directly as child of the scrollview
observe when the bounds of that contentview change
each time they change do a scrollRectToVisible to the bottom of the scrollview
disable the auto-scroll-to-bottom as soon as a scrollViewWillBeginDragging delegate call is detected so the auto-scrolling stops when the user starts moving the scrollview
I found a solution to this problem, this might be useful to future visitors. You need to load the tableView in viewWillAppear before trying to scroll.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.tableView.numberOfRowsInSection:0] inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
}