ContentSize Height of UIScrollView in iPhone X not updating - ios

This issue sounds easy at first but this is one of those things that I had to ask a question for suggestions since there is no other way I can think of to solve this. I already searched too far for solutions but did not find any.
Situation
I have a collection view that scrolls horizontally that covers the whole screen.
I added a scroll view inside each cell's content view that contains several views that will be taller than screen height.
After the last subview is added I update scroll view content size using:
scrollview.contentSize = CGSize.init(width: screenWidth, height: (scrollview.subviews.last?.frame.origin.y)! + (scrollview.subviews.last?.frame.size.height)!)
Let's say "scrollview" is the scroll view and "screenWidth" is the width of the screen. It works on other devices except iPhone X will scroll maybe for two screen heights worth but that's it. Other devices can go to the bottom of the scroll view.
While writing this I thought:
what if I need to add top and bottom padding of safe area to content size height since it extends there.
I did it but it only added so little and a big part of views still cannot be seen due to this issue.
Inputs are welcomed. Thanks in advance.

Related

How to fit a "freeform" UIViewController to multiple screen sizes?

I have created several freeform UIViewControllers that have extra long heights for scrolling purposes, and widths that equal the width of an iPhone 8 Plus screen (because that's the physical device I have available for testing with).
My problem comes when I am trying to make my app functional on all devices down to an iPhone SE sized screen. The height isn't an issue, but the width is creating a problem because on smaller screen sizes.
UIViewController is obviously wider than the screen. so, it allows for horizontal scrolling/panning. However, my goal is to have the ViewController match the width of the screen being used. so, only vertical scrolling is possible.
I've tried to adjust the width of the UIViewController when it gets presented, but that seems to have no effect. I'm really not sure what else to try and can't find anything about this online. Is this an obvious fix or am I doing something completely wrong?
To properly answer this question and for other people that might be having this issue.
The view controller size automatically adjusts to the screen size (provided its properly configured).
If you have an scrollview inside a view controller, then you should make sure the edges of the scrollview are attach to its parent view (the view controller's root view) using auto layout constraints.
In addition, the scrollview contents must be set to use the root view as the position reference (at least horizontally) otherwise auto layout won't know how to handle the horizontal positioning.
To accomplish this you just need to attach the inner view (that fills your entire scrollview) to the left and right edge of the view controller's root view as well.
I think you might be mistaking your UIViewController with a view. If your view is a scroll view, you can set the content size of the scroll view to be the same as the width of your device and it should achieve the desired effect.
In Swift 3.0 and above:
let screenWidth = UIScreen.main.bounds.width
yourScrollView.contentSize.width = screenWidth
To support multiple widths you can use different size classes available in iOS.
there are many tutorials available for size-class related constraints.
like https://www.bignerdranch.com/blog/designing-for-size-classes-in-ios/,
https://www.bignerdranch.com/blog/designing-for-size-classes-in-ios/
you should have to give View's constraints in relation to their Parent views if possible for your design.
I've tried to adjust the width of the UIViewController when it gets presented
By "presenting" you mean viewDidLoad method? I don't think it's the best place to adjust width because at this moment view bounds are not yet known. Besides viewDidLoad method is not called when devices is rotated.
I suppose you should update scroll view width in viewDidLayoutSubviews which is called every time when root view bounds changed in view controller:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let screenWidth = UIScreen.main.bounds.width
yourScrollView.contentSize.width = screenWidth
// I don't think you really need it
// but it may help
yourScrollView.setNeedsLayout()
}

UISlider causing horizontal scrolling

I have a simple screen, with a slider and a label positioned next to each other horizontally. I have embedded these inside a UIScrollView (I set this to fill the screen and used 'Add missing constraints'), because I will need vertical scrolling later down the line. I don't however, want horizontal scrolling. I have seen numerous posts on here and other sources about people wanting to disable horizontal scrolling, however I'm not sure that's what I want to do, I think I need to restrict the UISlider from causing the horizontal scrolling; I think it is trying to take up more width than the screen. I have added what I think are the necessary horizontal constraints:
Leading space to container for the UISlider
Horizontal spacing to the UILabel, and
Trailing space to container for the UILabel
But this still causes horizontal scrolling, and the UISlider's are the cause, they are taking up more room than I want, as seen below:
I have tried disabling horizontal scrolling in the code using a few techniques, one being:
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.x>0 {
scrollView.contentOffset.x = 0
}
}
but this does not seem to stop the horizontal scrolling.
Can anyone offer any suggestions?
Thanks in advance.
My suggestion is to never use Add missing constraints. It never does what you really want.
Here's the problem. You are laying out your UI on a ViewController in the Storyboard that is square. Apple did this to remind you that you need to be flexible in your design, but it doesn't match the size of any device. When you Add missing constraints, it uses the absolute dimensions of that square to create the constraints which are certainly wrong.
In your specific case, it is giving the slider a width that is too wide, which is why the slider goes off the right side of your screen.
Here's the trick about scroll views. If the contents inside of a scroll view are wider than the scroll view itself, then that content will scroll. The same applies vertically: if the contents inside of a scroll view are taller than the scroll view, then the contents will scroll.
In order to design this to work on all phones, you need to make sure that the contents of the scroll view are laid out correctly for each phone size. Which certainly means you don't want to use specific widths for both the label and the slider because you'll end up with the wrong width for some device, if not all of them.
The best way to do this is to:
Drag out the scroll view and add it to your ViewController. Add constraints to make sure it is properly sized on all phones, such as attaching it on all sides to its superview with a fixed distance.
Drag out a new UIView and drop it on the scroll view. Drag its edges until it exactly matches the size of the scroll view. This will be your content view. Pin all four edges of this content view to the scroll view with offsets of 0.
Here's a tricky bit. Even though you've pinned the content view to the scroll view, its size of free to grow because that is what allows it to be bigger than the scroll view itself and allow there to be content to scroll over. To keep your scroll view from scrolling horizontally, you need to make sure the content view has the same width as the scroll view on all devices. To do that, find the scroll view and the content view in the Document Outline to the left of the Storyboard. Control-drag from the content view to the scroll view and select Equal Widths from the pop-up.
You still haven't told your content view how tall it should be. For now, give it an explicit height constraint of 1000. That will be enough to scroll.
Now, add your label and slider to the content view. In addition to constraining them to each other and to the edges of the content view, you will need to give your label a width constraint. Then Auto Layout will have all of the information it needs to compute the width of your slider. Auto Layout knows how wide the content view is (which will be different on different devices), it knows how wide your label is, and how far everything is from everything else, so it will just stretch the slider to fill in the rest.
If you do all of this, you will have a UI that is properly sized for all devices in all orientations that scrolls vertically.
Just embed all view in your UIScrollView in a UIView, give it the required constraints then the slider and label will stay.
That worked for me just now.
UIScrollView is special when you want use AutoLayout with it, subviews can not be added directly, it needs a container view to constraint the contentSize of UIScrollView, Auto Layout Guide:Working with Scroll Views explains the detail reason, and you can find many solutions to solve UIScrollView's auto layout on Google, Such as this answer.
To be honest, it's confused and complicated to understand UIScrollView's auto layout, but if you overcome this, others auto layout question is easy to resolve.

uiscrollview--do I understand this correct?

ok, I have spent hours and hours trying to figure this out. read countless other questions on here, which have helped me understand how UI scrollview works, but which have not helped me actually implement one into my app. I think I finally understand--but it just doesnt seem correct. Can someone please tell me where I am going wrong (if I am) in the process below:
Have new, empty view controller.
add scrollview to view controller.
Put constraints on scrollview: Pin 0, 0, 0 and 0 to the view.
insert new UIview inside the scrollview. rename is contentView
put constraints on contentView: 0, 0, 0 and 0, all against the scrollview.
Now here is the tricky part: uiscrollview doesnt have a specific size--it only takes on the size of the container (contentView) that is inside it. So unless you give the contentView a certain height and width, then it says "scrollview has ambiguous scrollable height/width."
OK, so that makes sense. So I want the scrollable content height to be 900, so I set the height of the contentView to 900. But, I do not want horizontal scrolling. Users don't like it and it makes for an ugly user interface. But, the problem is that I have to set the contentView to a certain width, or else the width is 0 and no content shows up inside it.
So, I choose a certain width, for example 320. This works great for the iPhone 4S and 5/5S. And if the 4.7" and 5.5" iphone's didnt exist, this would be fine. But it gets a little awkward for the larger iphone's, because then the the content on the scrollview can't fully go across the screen, it abruptly ends and doesn't look presentable.
So, is there any way around this? I have been just using a tableview instead in the past, but now I need access to something that the uitablecell class can't do, and that's not an option. I tried setting the contentView to equal widths of the Superview, and that didnt work either, i still recieved the message "scrollview has ambiguous scrollable width." I just want the width of the contentView to be 320 for iphone4/5, and for it to be 400 for the 6/6 plus. Is this possible?
Constrain the scroll view's content view to be the same width as the root view (i.e. the one that contains the scroll view). Absolutely do not hard-code widths.
EDIT: Finally! Here's a screenshot of the storyboard I just created. I can put the project code on GitHub if anyone wants it.

UIScrollView zoom with Autolayout

I know there are a couple of questions around but I could not figure out how to do this (none of the answers there helped me), so here is my version of the question.
I have an UICollectionView which is set to scroll horizontally. Each cell has an UIScrollView inside and inside the UIScrollView I have an UIImageView.
Now, the images displayed by the UIImageView are loaded from the internet and I can't figure out a way to make the zooming work correctly using storyboards and autolayout.
If I set constraints that tie my UIScrollView to it's container everything is ok. The moment I tie the UIImageView to the UIScrollView XCode starts to complain that the UIScrollView's size is ambiguous.
If I don't make any constraints in InterfaceBuilder the images are not the displayed in the correct size (doh!).
So, I'm stuck. I don't know what kind of constraints to make and which view needs to be tied to what. I know that my perfect world result is a view controller that behaves like the native iOS Photos app. That is, the image is displayed as large as the screen (with a black band at top and bottom or left & right depending on image's orientation) and that you can zoom it in and pan it around.
Help please!
Note
I did read these posts before posting my own question
UIScrollView zooming with Auto Layout
UIScrollView Zoom Does Not Work With Autolayout
“Pinch to Zoom” using AutoLayout
I don't know if it'll help you with your zooming problem but Apple provided a technical note about using scroll views with auto layout: Technical Note TN2154, UIScrollView And Autolayout
The important part with scroll views is, that the constraints of the subviews inside the scroll view are not bound to the scroll view itself, but to the scroll view's parent (in your case the collection view cell)
It works
You can easily do it. Well not really easily, i have been struggling for quite a while!
I managed to implement auto layout within the scrollable area. Just it is vertical only
Set intrinsic size to a "placeholder" (to scrollable view)
Don't set contentSize at all
Attach right constraints to the wrapper (so it will be relative to the parent view)
self.view.addConstraint(NSLayoutConstraint(
item:self.view, attribute:.Trailing,
relatedBy:.Equal, toItem:contentView,
attribute:.Trailing, multiplier:1, constant:0))
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
So, in your case, your contentView right side should be attached the right side of the image
Try it out, certainly you will have your zooming to work
Here is an example, it may help you
https://github.com/nchanged/swift-content-manager/tree/master

UIScrollView offsetting content in contentView

I am having a very frustrating issue. I know there are all kinds of issues with UIScrollView in iOS7 and XCode 5. I need to implement a scrollview and there are all kinds of tutorials showing you how to do it by switching off auto layout but that then messes with the rest of the views in my app.
I tried the fix of putting my subviews into a container UIView and placing that in the UIScrollView and setting the scrollview's content size to the size of the contained UIVIew. That didn't work. Now I am working with placing everything in the scrollview and it all works with one exception. When I load the view on the simulator or a device the content view is moved down but somewhere around 60 points or so. See image below.
That white space below the title bar on the right is still the scroll view as I can press and drag within it. Adjusting the contentOffset doesn't do any good as that just scrolls the view down slightly. I have no idea what to do here.
Just a little more info: I setup the scrollview and all the subviews in storyboard and the connected them up. Not sure if that has any bearing on it.
Thanks in advance for any help you can provide.
I think your Scrollview top space constraint have 64 pixels. That's a problem. So, set your top space constraint value to 0.
You need to add the constraints before view displayed then contentsize will be set automatically.
Check UIScrollView's autoresizing mask. It has to be set like this.

Resources