SLComposeServiceViewController height issue - ios

My Share extension's view height seems to be rendered smaller than needed to accommodate the SLComposeSheetConfigurationItem. as you can see in attachment, it seems to be cut off from the bottom ("to: test#email.com") and I have to scroll it to see it all. Any idea why it might be happening. I am not customizing it in any way.

SLComposeServiceViewController stored SLComposeSheetConfigurationItem in tablView, so you need to access tableView by travelling on subView and call scrollToRow for bottom position will fix your issue.
Solution 1:
Add following code in ShareViewController.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let slSheet = self.children.first as? UINavigationController,
let tblView = slSheet.children.first?.view.subviews.first as? UITableView
{
// Scroll tablView to bottom
tblView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .bottom, animated: true)
}
}
Solution 2:
Change the row height will fix your issue, But make sure it is exactly height value which you want, I have used as per my requirements. Default value is 44.
tblView.rowHeight = 35
tblView.separatorStyle = .none

This seems to be an issue with iOS 13. The sheet's height is controlled by the system. Setting the preferredContentSize property doesn't have any effect. I'm using the following workaround:
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 13.0, *) {
_ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidShowNotification, object: nil, queue: .main) { (_) in
if let layoutContainerView = self.view.subviews.last {
layoutContainerView.frame.size.height += 10
}
}
}
}
The last subview of self.view is an instance of UILayoutContainerView, which is a private UIKit class. This class determines the size of the share dialog, but doesn't have any constraints attached to it. Therefore I have to set the frame property directly. I'm using the keyboardDidShowNotification callback because the frame is updated by the system when the keyboard appears.
If anyone comes up with a better solution, please let me know.

Related

Extra space is getting added in UITableView top while updating tableview datasource property

When I try to update the tableview datasource property as shown below
override func viewWillAppear(_ animated: Bool) {
tableView?.delegate = self
tableView?.dataSource = self
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
tableView?.delegate = nil
tableView?.dataSource = nil
super.viewWillDisappear(animated)
}
I am getting an extra space on top of the Tableview when I navigate to other screen and come back to this screen, can anyone help resolving the issue ?
To
Note* I am tried this in simulator with version 10.2
Try to return the header height as 0. This works most of times. If not also try setting an empty view with 0 height to the header view of the table view.
If you are using the AutomaticDimension to update the cell heights. Make sure to set the .estimatedHeight property to the minimum height you cell can have.

how to move inputView above keyboard when scrolling the table

I have a view controller in which it has
table view and textView below the table
When I tap on the textView the keyboard appears and when I tap outside the textView the keyboard disappar
That works fine but I have one problem which is when I Drag(scroll Down) the table view the textView doesn't move down with the keyboard and I see black background behind the keyboard as in the below image
How to solve this problem in Swift
Update:
This is my current code that observe the keyboardFrameChanges
func keyboardFrameChanged(notification: NSNotification) {
let dict = NSDictionary(dictionary: notification.userInfo!)
let keyboardValue = dict.objectForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let bottomDistance = mainScreenSize().height - keyboardValue.CGRectValue().origin.y
let duration = Double(dict.objectForKey(UIKeyboardAnimationDurationUserInfoKey) as! NSNumber)
UIView.animateWithDuration(duration, animations: {
self.inputViewConstraint!.constant = -bottomDistance
self.view.layoutIfNeeded()
}, completion: {
(value: Bool) in
self.chatTableView.scrollToBottom(animation: true)
})
}
Update 2:
I found a solution by changing the keyboardDismissMode from Interactive to OnDrag
chatTableView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.OnDrag
This will move the keyboard and the textView immediatly to down once it observe any drag movement in the table , But how to do it in the Interactive mode like in the whatsapp chat view
set you scroll view (UITableView or UICollectionView) frame equal to you UIViewController frame
set .interactive to keyboardDismissMode of you scroll view
override canBecomeFirstResponder and return true
setup input container with UITextField or UITextView in screen bottom
override inputAccessoryView and return input container
enjoy
See my simple code here on github
If you implement your own bottom constraint for input view
And change its constant on the keyboard frame notifications. Using these lines of code, the error of predictive bar layout at the end of the animation will be fixed.
override var inputAccessoryView: UIView? {
return UIView()
}
override var canBecomeFirstResponder: Bool {
return true
}
Finding the answer for this took forever but this is the correct way of implementing this feature with the interactive state enabled.
In your view controller that has the tableview or collection view displaying chat bubbles override inputAccessoryView, then return the view that contains the uitextView or uitextField. and override canBecomeFirstResponder then return true.
override var inputAccessoryView: UIView? {
get {
self.inputBar.frame.size.height = self.barHeight // 50.0
self.inputBar.clipsToBounds = true
return self.inputBar
}
}
override var canBecomeFirstResponder: Bool{
return true
}
You could observe the frame of the keyboard using the NSNotificationCenter and create a method which will be called when the frame changes. In this method you get the coordinates of the keyboard and can reposition your inputView accordingly.
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardDidChangeFrame(_:)), name: UIKeyboardWillChangeFrameNotification, object: nil)
}
func keyboardDidChangeFrame(notification: NSNotification) {
let keyboardScreenFrameEnd = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
let keyboardViewFrameEnd = view.convertRect(keyboardScreenEndFrame, fromView: view.window)
let keyboardHeight = keyboardViewFrameEnd.height
// calculate the offset of your inputView
inputView.frame = CGRectOffset(inputView?.frame, ..., ...)
}

UIScrollView doesn't move the view properly when the keyboard appears

I have a view controller with a UIScrollView pinned to all 4 sides. Then a UIView inside with all its 4 sides pinned to the scroll view and as well as equal width and equal height constraints added.
Inside this view, there are two container views. These two container views embed two separate UITableViewControllers. I'm getting no auto layout errors or warnings.
This is how it looks when it's run.
In the bottom table view, one cell(middle one of the first section) has a UITextField and the bottom cell has a UITextView. So obviously when the keyboard appears, these fields get obscured.
So what I wanted to do was to move the entire view that contains both container views when the keyboard appears. That's why I embedded it inside a scrollview. I use this code to monitor keyboard showing/hiding and set the scrollview's content inset accordingly.
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
adjustInsetForKeyboard(true, notification: notification)
}
func keyboardWillHide(notification: NSNotification) {
adjustInsetForKeyboard(false, notification: notification)
}
func adjustInsetForKeyboard(show: Bool, notification: NSNotification) {
let userInfo = notification.userInfo ?? [:]
let keybaordFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
let adjustmentHeight = (CGRectGetHeight(keybaordFrame)) * (show ? 1 : -1)
scrollView.contentInset.bottom += adjustmentHeight
}
}
But there are a couple of issues.
When the keyboard appears and although I change the scrollview's content inset, the entire view doesn't move. It does this weird thing. The bottom tableview goes under the top table view. It's easier to show so here is a video.
Tableviews overlapping issue
When I refocus on a textfield for more than 1 time, the scrollview goes off the screen!
Tableview going off the screen
Anyone got an idea why this is happening?
Dropbox link to demo project
A UITableViewController already automatically handles the adjustment of the content inset when the keyboard is shown. There is no documented way to disable this behaviour. You can override viewWillAppear(animated: Bool) in your StaticTableViewController and not call it's super method:
override func viewWillAppear(animated: Bool) {
}
It's probably where the UITableViewController registers for the keyboard events, as this disables the content inset adjustment. However, I can't tell you if there will be other adverse effects of not calling viewWillAppear of UITableViewController and this behaviour might change with future versions of iOS. So a safer way is to just not use UITableViewController and add a standard UITableView to a UIViewController and load your cells in there.
Also note that with your design the user could scroll all the way up and hide your lower content view behind the keyboard. Then the user can't scroll down as any scrolling only scrolls and bounces the upper tableview. So rethink your design or hide the keyboard as soon as the user scrolls
There are couple ways:
To observe UIKeyboadWillShowNotification and UIKeyboardWillHideNotification, get keyboard size data from it and adjust your scrollView contentInset bottom value properly.
func viewDidAppear() {
super.viewDidAppear()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "increaseContentInset:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "decreaseContentInset:", name: UIKeyboardWillHideNotification, object: nil)
}
func viewDidDisappear(){
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func increaseContentInset(notification: NSNotification) {
let endRect = notification.userInfo![UIKeyboardFrameEndUserInfoKey]
scrollView.contentInset = UIEdgeInsetsMake(0, 0, CGRectGetHeight(endRect), 0)
}
func decreaseContentInset(notification: NSNotification) {
scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
Use the library for it. I strongly recommend you to use TPKeyboardAvoiding

UITextView is not scrolled to top when loaded

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

UITextView starts at Bottom or Middle of the text

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.

Resources