I have a nib with a full screen textView. In viewDidLoad,
self.textView.attributedText = //some text;
Its as simple as this and works fine except for iphone landscape mode.
In landscape mode, when I navigate to this page, contentOffset.y of this textView is not initilized to zero. So, by default the scroll position is at the middle of the content (I expected this to be at the start of the content).
For ipad and iphone portrait mode, scroll position is at the start of the content (contentOffset.y is zero)
In iOS7, to scroll to the top, you must take the content insets into account. I also found it was necessary to wait for the next runloop iteration, as Altaveron says.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_textView.contentOffset = CGPointMake(-_textView.contentInset.left, -_textView.contentInset.top);
});
In iOS 7, I was also having the same issue. UITextView's scroll starts in the middle in the landscape view. I have navigationBar at the top of the view controller. To solve this issue, I've set the contentOffset in viewDidLoad as follows:
[self.textView setContentOffset: CGPointMake(0,-200) animated:NO];
I've subtracted 200 to balance with my navigation bar. And it worked for me. Since it is set in viewDidLoad, y offset is not reset to 0 if the user changes orientation.
Hope it helps!
The following workaround helped me. setText calls UITextView setText method.
[self performSelector:#selector(setText) withObject:nil afterDelay:0.f];
On iOS8 or older version of iOS, using textView.contentOffset = CGPointZero.
But from iOS 9 i think we need to update it on viewDidLayoutSubviews()
My similar issue was solved with the following:
textView.contentOffset = CGPointMake(0.0, -50)
On certain devices, the textView would initially be slightly scrolled down. But I needed it to be scrolled to the top.
For some reason, setting it to CGPointZero did not have any affect. However, setting the offset y to a large negative number did the trick.
I have a UINavigationController in my app. The UINavigationBar is set to opaque and all the scroll views do not overlap underneath the bar.
In one view I have a UITableView. The frame of the UITableView is (0 0; 320 504) on my iPhone 5. i.e. the height is 568 - 64 (the height of the nav bar and status bar).
The contentInset of the UITableView is (0, 0, 0, 0). When the table view first loads the contentOffset is (0, 0).
This is fine. Works brilliantly.
I added a UIRefreshControl to the table view. This works a couple of times but then after a few times of doing pull to refresh then the content at the top gets "stuck" under the nav bar.
When this happens I inspect the contentInset and it is now (-60, 0, 0, 0).
Is there any way to stop the UIRefreshControl from changing the contentInset?
This is probably the reason why UIRefreshControl is currently only supported on UITableViewController, rather than by addition to any scrollview (which you can get away with, in many cases).
The refresh control does its magic by tinkering with the content insets of the scrollview - particularly when it ends refreshing. Unfortunately the view controller is also tinkering with the content insets of the scroll view to fit it under the translucent nav and status bars. Fun ensues. Is this also an issue on iOS 6 (or, "good old iOS6" as I called it when dealing with the same issue).
The quickest solution is probably to add your table view as a child UITableViewController instead of a simple subview. I think that UITableViewController manages the insets for you at the end of the refresh. If that doesn't work, I've got workarounds for this but it will have to wait until I get back in the office.
I will add this answer here in case any one has problems with UIRefreshControl by changing the control properties (attributed title, tint, etc...):
Don't mess with the UIRefreshControl on -viewDidLoad:, use -viewDidAppear: instead.
Reset your table view contentInset.
-(void)pullToRefresh
{
[self.tableView reloadData];
[self.refreshControl endRefreshing];
[self.tableView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
}
You need override setContentInset: in you UICollectionView
- (void)setContentInset:(UIEdgeInsets)contentInset {
if (self.tracking) {
CGFloat difference = contentInset.top - self.contentInset.top;
CGPoint translation = [self.panGestureRecognizer translationInView:self];
translation.y -= difference * 3.0 / 2.0;
[self.panGestureRecognizer setTranslation:translation inView:self];
}
[super setContentInset:contentInset];
}
I have dragged a plain jane UITableView onto a UIViewController in iOS 7.
Now there is an vertical offset of space before the first cell starts. How do I get rid of it? I want the first line to be much closer to the top edge of where the UITableView actually starts. I did not ask for the large offset did I?
Any ideas?
The new iOS 7 implementation of UIViewController has a new set of options that allows the developer to choose if the system will automatically add insets for UIScrollView, UITableView and derivations.
To disable this behaviour uncheck these boxes for all your wanted UIViewControllers in InterfaceBuilder, on UIViewController selected object inspector:
For more details:
Submit your iOS 7 apps today.
iOS 7 UI Transition Guide > Appearance and Behavior
By default table view controllers will pad the content down under the nav bar so you could scroll the content under it and see it, in a blurred state, underneath the navbar/toolbar.
Looks like you're positioning it at 44 (maybe 64)px to move it out from under the nav bar, but it already compensates for this so you get a big gap.
Go to the storyboard/xib in IB and untick the show content under nav bar stuff.
From iOS7 transition guide:
If you don’t want a scroll view’s content insets to be automatically
adjusted, set automaticallyAdjustsScrollViewInsets to NO. (The default
value of automaticallyAdjustsScrollViewInsets is YES.)
self.automaticallyAdjustsScrollViewInsets = NO;
i had a similar problem, after dismissing a viewController, the contentOffset from my tableView was changed to (0, -64).
my solution was a little weird, i tried all the other answers but had no success, the only thing that fixed my problem was to switch the tableView position in the controls tree of the .xib
it was the first control in the parent View like this:
I moved the tableView right after the ImageView and it worked:
it seems that putting the table view in the first position was causing the trouble, and moving the table view to another position fixed the problem.
P.D. I'm not using autoLayout neither storyboards
hope this can help someone!
it resolve my similar problem:
if ([[UIDevice currentDevice].systemVersion floatValue] >= 7){
tableView.contentInset = UIEdgeInsetsMake(-20, 0, 0, 0);
}
Try using this
tableView.separatorInset = UIEdgeInsetsZero;
Obviously, if you're supporting anything less than iOS7 you will need to ensure that the object responds to this selector before calling it.
Seriously, changing contentOffset is not the solution. You're just patching a problem and not fixing the cause. I've been facing the same problem and it turns out that grouped tableViews have a padding on the top. In my case setting the type to plain has done the trick.
Hopefully it saves someone a few minutes.
Z.
With iOS 9, none of the other answers from this page worked for me (i.e. unchecking boxes in Storyboard, setting automaticallyAdjustsScrollViewInsets to NO).
My workaround I am really dissatisfied about was this:
- (void)viewDidAppear:(BOOL)animated {
self.tableView.contentOffset = CGPointMake(0.0, 0.0);
self.tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0);
}
The same lines in viewWillAppear or viewDidLoad were ineffective.
Sometimes, I get a 64 height gap at the top of my Table View when the UIViewController is embedded inside a Navigation Controller.
In the past, I would just re-create everything, hoping that the constraints turn out correct after a clean slate.
TIL: If you don't want to make a vertical constraint to the Top Layout Guide, you can hold down the Option key to access the Container Margin.
Then make sure the Top Space to Superview constant is set to 0. This worked for me at least.
THis works for me:
- (void)loadView
{
[super loadView];
[self setAutomaticallyAdjustsScrollViewInsets:YES];
self.edgesForExtendedLayout = UIRectEdgeNone;
self.view.frame = CGRectZero;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
If you add an empty UIView before UITableView (or any view is scrollable such as ScrollView and TextView), you can have a luck.
I had a UITableViewController embedded in a container view. To get rid of the unwanted 64 points of vertical space, I needed to uncheck the 'Adjust Scroll View Insets' in Interface Builder and set the UITableView's contentInset in my UITableViewController's viewWillAppear as below.
The size of the vertical space appears to match the navigation bar's frame height and y offset. The problem only occurred on iOS 7.
- (void)viewWillAppear:(BOOL)animated;
{
[super viewWillAppear:animated];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
const CGRect navBarFrame = self.navigationController.navigationBar.frame;
const CGFloat blankVerticalSpace = navBarFrame.origin.y + navBarFrame.size.height;
self.tableView.contentInset = UIEdgeInsetsMake(-blankVerticalSpace, 0, 0, 0);
}
}
In Xamarin iOS, I had this issue occuring on a backgrounded UITableViewController just after the foreground modal dialog was being dismissed. In the process of moving into the foreground, the UITableViewController had insets set (somewhere by iOS):
This class solved it
public class UITableViewControllerWithBugFix : UITableViewController {
public UITableViewControllerWithBugFix(UITableViewStyle withStyle) : base(withStyle) {
}
public override void ViewWillLayoutSubviews() {
if (TableView.ContentInset.Top != 0.0f)
TableView.ContentInset = UIEdgeInsets.Zero;
if (TableView.ScrollIndicatorInsets.Top != 0.0f)
TableView.ScrollIndicatorInsets = UIEdgeInsets.Zero;
base.ViewWillLayoutSubviews();
}
}
From time to time I get back to this awful situation, and I notice that it's still quite unknown. So, for future memory...
Lately, I'm fixing with this workaround.
Add a subview at the top of the UITableView, with zero pixel height. Works with Autolayout or without.
If I feel confident :-) I remove this fake view, fix the constraints on the top, and magically it works.
Don't ask me why, I still think it's a bug deep in UIKit.
If you go to storyboard, you can change the offset by selecting the table and under the Table View section in the Attributes inspector you just change the Separator Insets on the left to 0.
I think the real solution is to set up your top and bottom constraints on the tableview be equal to the topMargin and bottomMargin. Not the top layout guide and bottom layout guide. This allows you to keep automaticallyAdjustsScrollViewInsets to be true.
Swift 3 solution:
class PaddingLessTableView : UITableView
{
override func headerView(forSection section: Int) -> UITableViewHeaderFooterView?
{
return nil
}
override func footerView(forSection section: Int) -> UITableViewHeaderFooterView?
{
return nil
}
}
If you embedded your TableViewController in a NavigationController or a ContainerView, You have to constraint to margins instead of top Layout guide in Storyboard. Check constraint to margins when you are doing the constraints No other way worked for me. I couldn't comment so I'm just reiterating on Robert Chens answer.
I tried several of the answers. Changing the settings in storyboard caused ripple problems with an overlay menu that pops in from the left.
I only have a blank UIViewController in storyboard, otherwise everything is programmatically generated.
I have same problem with a UITableView inside a UIView inside a UIViewController. Namely, the section headers start too far down when the UIViewController is embedded in a Navigation Controller. W/o the navigation controller everything works fine.
To fix the problem I created a UILabel and with constraints placed the UILabel bottom constraint = the top constraint of the UIView (so it does not show on the screen. Now with that additional control (the new Label) the TableView behaves properly.
inputsContainerView.addSubview(titleLabel)
inputsContainerView.addSubview(tableView)
// inputsContainerView
///////////////////////////////////////
inputsContainerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
inputsContainerView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
inputsContainerView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true
inputsContainerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.7).isActive = true
// tableView
///////////////////////////////////////
tableView.centerXAnchor.constraint(equalTo: inputsContainerView.centerXAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: inputsContainerView.topAnchor).isActive = true
tableView.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true
tableView.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor).isActive = true
// titleLabel - inserted to stop bad section header behavior
///////////////////////////////////////
titleLabel.centerXAnchor.constraint(equalTo: inputsContainerView.centerXAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: inputsContainerView.topAnchor).isActive = true
titleLabel.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
I had a same problem in iOS 11 and xib, UITableviewController and I solved it as below
[self.tableView setContentInset:UIEdgeInsetsMake(-44,0,0,0)];
If none of the above answers work, try changing the table view style to plain from grouped
Forgive me to the obtuse title, as I'm unsure how to describe this question.
Recently many iOS apps utilise a scrolling UI design pattern which helps to maximise screen real-estate, typically hiding the header when the user scrolls downwards.
For example, Instragram's main view has the Instragram header at the top. Scrolling upwards on this view keeps the header fixed at the top, and the view bounces back normally to the top of the content. But scroll down and the header acts as part of the content, making way for an extra 44 points of vertical space.
Its probably that I haven't done much iOS work in a while, but I can't easily figure out how best to impliment this? Apologies for the terrible description.
If the header stays put no matter what, use a separate view on top of the scroll view.
If you use UITableView, you can use section headers.
EDIT Use this code:
- (void)scrollViewDidScroll:(UIScrollView*) scrollView
{
CGPoint offset = scrollView.contentOffset;
CGRect headerFrame = _headerView.frame;
if(offset.y > 0){
headerFrame.origin.y = offset.y;
}
else{
headerFrame.origin.y = 0.0;
}
[_headerView setFrame:headerFrame];
}
(Assumes _headerView is your header, sitting on top of the scroll view, not inside it. Also, both scroll view and header begin at the top of their parent view, y==0. Also, your view controller must be set up as delegate of the scroll view)
I just wrote this code from memory; haven't tested it but at most it should only need tweaking.
I tried ranReloaded's answer above but it seems that calling setFrame: on a UIScrollView stops the view from bouncing when going beyond its bounds.
Instead I set the scroll view to fit inside another UIView called scrollerWrapper. Applying the calculated origin and height to this view gives me effect I'm after plus retains the bounce behaviour.
- (void)scrollViewDidScroll:(UIScrollView*) scrollView
{
CGPoint offset = scrollView.contentOffset;
CGRect headerFrame = header.frame;
CGRect wrapperFrame = scrollerWrapper.frame;
if(offset.y > 0){
headerFrame.origin.y = -offset.y;
wrapperFrame.origin.y = MAX(0, headerFrame.size.height - offset.y);
}
else{
headerFrame.origin.y = 0.0;
wrapperFrame.origin.y = headerFrame.size.height;
}
wrapperFrame.size.height = self.view.frame.size.height - wrapperFrame.origin.y;
[header setFrame:headerFrame];
[scrollerWrapper setFrame:wrapperFrame];
}
So a little explanations of what my view is doing before the problem, the page loads a bunch of buttons that are clickable into the contentView which is a subView of my scrollView.
i got a UIViewController, inside i have my
UIScrollView scrollView to handle the scrolling and zooming
UIView contentView which i add all the UIButtons to.
Alright i'll try to only put the code which i believe might need to be changed.
in viewDidLoad
scrollView.contentSize = CGSizeMake(320, (20+totalRows*OFFSET_Y) );
[scrollView setMaximumZoomScale:4];
[scrollView setMinimumZoomScale:1];
[scrollView setDelegate:self];
[contentView setFrame:CGRectMake(0, 0, 320, (20+totalRows*OFFSET_Y) )];
[self.scrollView addSubview:contentView];
Doing some calculation to see how big i need to content size to be to fit all the buttons, then i add the contentView as subView to my scrollView.
I then add all the UIButtons as subview of my contentView
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return [self contentView];
}
Got my delegate method for zooming returning the contentView.
Ok so everything is scrollable and zoomable like it should be..all my UIButtons are still clickable and works how it should be.
The problem is after i zoom in/out when i scroll all the way to the bottom of my scrollView it's being cut off from the bottom, so the last row of buttons are being cut in half. even when i rezoom to 1:1 it's still cut off.
I've got a NavigationController in the app which is 44 pixels, not sure if that's screwing something up somehow.
I was checking the contentSize of the scrollView and contentView and before any zooming is done, the Height of my scrollView is 44 pixels bigger than the contentView, but after any zooming the ratio is 1:1 and that seems to be the problem.
I'm not changing the size anywhere in the code though.
Any help is appreciated!
Thanks
Like i thought before I added this piece of code
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale (float)scale
{
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, self.scrollView.contentSize.height + (44 * scale));
}
And now Zooming doesn't cut anything off and seems to be working correctly.
I still don't believe this is what I should have to do but I guess it works for now.
Still hoping someone has a better answer!
After playing more with my app I realized that the zooming was never the problem to begin with. The actual problem was with my UIView *contentView. After changing the background colour of my scrollView and my contentView (to 2 very diff colours) I noticed that my contentView wasn't long enough to cover all of the buttons.
So at first load the scrollView was big enough to see everything but once you zoom and it takes the size of the contentView it wasn't being adjusted properly!
All that to say, it finally works how it should!..haha no replies but hopefully someone might find this useful! Best tip was to change the background colours.
Good Day,
I realize very late to the game, but wanted to updated what i found on this, should anyone else come across this issue.
All did was set BOTH the UIImageView and the UIScrollView to "Aspect Fit" and this fixed the issue for me. This may be an Xcode 7 feature, but it works.