UIScrollView bounces back to top - ios

Inside a UIScrollView I have Container UIView that is larger than screen and when I scroll even a little it automatically bounces back to top after I release it.
I have set margins of Container UIView and UIScrollView to 0 and add the constraints Equal Height and Equal Width to Container UIView in relation to the Main UIView.
I saw that many people asked similar question but none of answers to their question helped me.
Do you know how to fix this?

Make sure your constraints are appropriate. As you have already mentioned that the container view's height is greater than the screen (also greater than the scroll view), you can try setting the contentSize of the ScrollView manually in viewDidLayoutSubViews.
-(void)viewDidLayoutSubviews
{
self.scrollView.contentSize = self.containerView.size;
}
If it does not work, then try using the following:
-(void)viewDidLayoutSubviews
{
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
}

Related

UiScrollView Incorrect Height (Without AutoConstraint)

Searched a lot on stack overflow, but didn't find any solution that works for us.
What we have are a couple of Views and UILabels and UIButtons in UIScrollView as below format. On Click of button we are hiding couple of views and labels and trying to recalculate UIScollView height.
UIScrollView
-->UIView
-->UIView2
-->UIView3
-->UILabel1
-->UILabel2 ( Please note these labels are not inside UIView, they are directly added to scrollview. Is this correct approach or they should be added inside a UIView?)
-->UITextField (Directly added to scrollview)
-->UITextField1 (Directly added to scrollview)
-->UIButton ( On Click of Button above textfields and labels are hidden or shown based on business logic)
When we try to reset size of UIScrollview on UIButton click it doesn't calculate height correctly. Tried below solution shared by lot of answers in stack overflow
Is the problem with labels and buttons added directly to UIScrollView?
Is there better way to set height of UIScrollview correctly?
We don't want to use AutoConstraint
CGRect contentRect = CGRectZero;for (UIView *view in uiScrollViewObj.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
uiScrollViewObj.contentSize = contentRect.size;
No it's not, in fact UILabels and UIButtons are UIViews.
I think you are confusing two concepts. The UIScrollView height is the height of the viewport (the visible part of the scrollview). What you want to change is the contentSize height that is the height of the whole content which you want to scroll through (I don't know if I have explain myself clear).
If the structure is the way you said it, so the UIButton is the closest element to the button, you should move its frame and place it upper, otherwise the contentSize is always gonna be the same.
Besides that, your code is iterating through all the views of the scrollview and those are not only the ones you added but also the ones that Apple inserted.
The best way to calculate the contentsize dynamically is to sum scrollview subviews y position and their height. Something like this should work:
CGFloat viewY, maxY=0;
for (UIView *view in uiScrollViewObj.subviews) {
if (view.hidden) continue;
viewY = view.frame.origin.y + view.frame.size.height;
maxY = (viewY > maxY) ? viewY : maxY;
}
[uiScrollViewObj setContentSize:CGSizeMake(uiScrollViewObj.frame.size.width, maxY)];

UIView in UIScrollView not scrolling

I am using the Xcode storyboard interface builder and I created a view controller with a UIScrollView that only has a single child UIView. This child UIView has many labels/buttons etc attached and exceeds the screen size height wise, however when I run my app, the view does not scroll for some reason.
Here is a picture of the ViewController structure:
What am I doing wrong?
You need to set the contentSize property of the UIScrollView. You can do this programatically (e.g. in viewDidLayoutSubviews) or under User Defined Runtime Attributes in the identity inspector panel in interface builder.
The actual size is dependent on your content. You can calculate it programatically using something like:
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect;
which will just find the smallest CGRect that contains all the frames of the subvies of the UIScrollView.
You can set the contentSize programmatically, but you don't need to and shouldn't have to. Instead, ensure that you have enough constraints that the scroll view's child view can calculate an exact total width and height. If you don't have enough constraints defined to do this, then Interface Builder will show you an error.

IOS - Keep UIView on top of UITableView on scroll

I have a UITableView with a UIView on top. I want the UIView to stick to the top as the tableView cells scroll over it.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (self.tableView.contentOffset.y > 0) {
CGRect newframe = self.publicTopView.frame;
newframe.origin.y = -self.tableView.contentOffset.y;
self.publicTopView.frame = newframe;
NSLog(#"After: %f", self.publicTopView.frame.origin.y);
}
}
You need to set your table view header view to the view you want on top.
Add this code to you viewDidLoad
self.tableView.tableHeaderView = self.publicTopView
I'm not certain what you're trying to accomplish, but I have a guess at what is wrong. As you scroll your contentOffset will continue to change and let's say your tableView has a content size height of 1500, then your contentOffset will eventually be larger than the height of your view controllers view. Now see that you are putting that contentOffset into the origin.y of your publicTopView. So your publicTopView could possibly be moving too much, even offscreen depending on how large your tableview's content size is.

UIScrollView not scrolling in iOS7 with autolayout on

I have a UIScrollView with a 6 textfields in it and a button inside of it. There is not enough content in the scrollView to make it scroll.
But when the keyboard shows, I would like the scrollview to scroll so the user doesn't have to dismiss the keyboard in order to select another textfield that is hidden by the keyboard.
I am using iOS7 and have autolayout enabled.
Any suggestions?
I am using storyboards and the only code I have is the following.
reg.h file
interface registerViewController : UIViewController <UITextFieldDelegate, UIScrollViewDelegate>
In order to make a scrollview scrollable, the content size must be larger than the scrollview's frame so the scrollview has something to scroll to. Use setContentSize to adjust the content size:
[scrollview setContentSize:CGSizeMake(width, height)];
In this case, you should adjust the size to view.frame.width, view.frame.height + keyboard_height, then adjust the content offset once the keyboard appears:
[scrollview setContentOffset:CGPointMake(0, 0 - keyboard_height)];
If for some screwy, autolayout-related reason this still doesn't make the view scrollable, implement this setContentSize function in viewDidLayoutSubviews in order to override the autolayout:
- (void)viewDidLayoutSubviews {
[scrollview setContentSize:CGSizeMake(width, height)];
}
EDIT: To reset the scrollview after dismissing the keyboard, reset the scrollview content size to the scrollview's frame and the offset to zero:
[scrollview setContentSize:CGSizeMake(scrollview.frame.size.width, scrollview.frame.size.height)];
[scrollview setContentOffset:CGPointZero];
P.S. To animate the content offset, use:
[scrollview setContentOffset:offsetSize animated:YES];
There is a contentInset property of UIScrollViews, you can set the contentInset to make additional space at the bottom to allow for scrolling without changing contentSize.
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, 100, 0.0);
scrollView.contentInset = contentInsets;
above code adds 100 points inset at the bottom.
By the way, there is an official document about this matter. It explains everything you should do. You can find it here. You can find what you are looking for under the section 'Moving Content That Is Located Under the Keyboard'
Try
Create Scroll view
Add View to the scroll view (In my case i added view as mainView).
Set ScrollView autoresizing.
Set MainView autoresizing.
To set the Scroll content Size equal to the view created add below line
Add the below line
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.scrollView.contentSize = self.mainView.frame.size;
}

UIScrollView with scaled UIImageView using autolayout

Consider a UIScrollView with a single subview. The subview is an UIImageView with the following size constraints:
Its height must be equal to the height of the UIScrollView.
Its width must be the width of the image scaled proportionally to the height of the UIImageView.
It is expected that the width of the UIImageView will be bigger than the width of the UIScrollView, hence the need for scrolling.
The image might be set during viewDidLoad (if cached) or asynchronously.
How do you implement the above using autolayout, making as much use as possible of Interface builder?
What I've done so far
Based on this answer I configured my nib like this:
The UIScrollView is pinned to the edges of its superview.
The UIImageView is pinned to the edges of the UIScrollView.
The UIImageView has a placeholder intrinsic size (to avoid the Scrollable Content Size Ambiguity error)
As expected, the result is that the UIImageView is sized to the size of the UIImage, and the UIScrollView scrolls horizontally and vertically (as the image is bigger than the UIScrollView).
Then I tried various things which didn't work:
After loading the image manually set the frame of UIImageView.
Add a constraint for the width of the UIImageView and modify its value after the image has been loaded. This makes the image even bigger (?!).
Set zoomScale after the image is loaded. Has no visible effect.
Without autolayout
The following code does exactly as I want, albeit without autolayout or interface builder.
- (void)viewDidLoad
{
{
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:scrollView];
self.scrollView = scrollView;
}
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height)];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.scrollView addSubview:imageView];
self.scrollView.contentSize = imageView.frame.size;
self.imageView = imageView;
}
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self layoutStripImageView];
}
- (void)layoutStripImageView
{ // Also called when the image finishes loading
UIImage *image = self.imageView.image;
if (! image) return;
const CGSize imageSize = image.size;
const CGFloat vh = self.scrollView.frame.size.height;
const CGFloat scale = vh / imageSize.height;
const CGFloat vw = imageSize.width * scale;
CGSize imageViewSize = CGSizeMake(vw, vh);
self.imageView.frame = CGRectMake(0, 0, imageViewSize.width, imageViewSize.height);
self.scrollView.contentSize = imageViewSize;
}
I'm trying really hard to move to autolayout but it's not being easy.
Under the autolayout regime, ideally the UIScrollView contentSize is solely determined by the constraints and not set explicitly in code.
So in your case:
Create constraints to pin the subview to the UIScrollView. The constraints have to ensure the margin between the subview and the scroll view are 0. I see that you have already tried this.
Create a height and a width constraint for your subview. Otherwise, the intrinsic size of the UIImageView determines its height and width. At design time, this size is only a placeholder to keep Interface Builder happy. At run time, it will be set to the actual image size, but this is not what you want.
During viewDidLayoutSubviews, update the constraints to be actual content size. You can either do this directly by changing the constant property of the height and width constraint, or calling setNeedsUpdateConstraints and overriding updateConstraints to do the same.
This ensures that the system can derive contentSize solely from constraints.
I've done the above and it works reliably on iOS 6 and 7 with a UIScrollView and a custom subview, so it should work for UIImageView too. In particular if you don't pin the subview to the scroll view, zooming will be jittery in iOS 6.
You may also try creating height and width constraints that directly reference a multiple of the height and width of the scroll view, but I haven't tried this other approach.
translatesAutoresizingMaskIntoConstraints is only required when you instantiate the view in the code. If you instantiate it in the IB it's disabled by default
In my opinion the UIImageView should fill the ScrollView. Later I'd try setting the zoom of the scrollview to the value that suits you well so the image can only be panned in one direction
In my case it was a full width UIImageView the had a defined height constraint that causing the problem.
I set another constraint on the UIImageView for the width that matched the width of the UIScrollView as it is in interface builder then added an outlet to the UIViewController:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageViewWidthConstraint;
then on viewDidLayoutSubviews I updated the constraint:
- (void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.imageViewWidthConstraint.constant = CGRectGetWidth(self.scrollView.frame);
}
This seemed to do the trick.

Resources