Adjust UIScrollView's contentSize with an embedded UIView? - ios

I have a UIScroll view which contains only one subview. The subview called contentViewis an UIView. Here is what I did in viewDidLoad():
self.scrollView = UIScrollView()
self.view.addSubview(self.scrollView)
self.scrollView.backgroundColor = UIColor.yellowColor()
// pin all edges to the edges of the superview (self.view)
self.scrollView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.view)
}
// create contentView
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.redColor()
self.scrollView.addSubview(self.contentView)
// pin the edges of the contentView to the scrollView
self.contentView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.scrollView)
}
let myView = UIView(frame: CGRectMake(100, 100, 20, 300))
myView.backgroundColor = UIColor.greenColor()
self.contentView.addSubview(myView)
The result is this:
There is ne red contentView as shown in the previous screen shot.
Next, I tried to adjust the size of the contenView in viewDidLayoutSubviews():
let newSize: CGSize = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
println("newSize: \(newSize)")
The result is:
newSize: (0.0, 0.0)
How can I either setup the correct size of the contentView or set the correct size of the UIScrollView's contentSize?

have you tried setting the var contentSize: CGSize of the UIScrollView? The documentation says
Discussion The unit of size is points. The default size is CGSizeZero.
If you don't know the size of the content view, then I would recommend setting
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth| UIViewAutoresizingFlexibleHeight);
and
scrollView.autoresizesSubviews = YES;
for good measure.

Related

IOS Swift4 Not able to scroll a ScrollView

I hv been trying to make a scrollview scroll, just to the extent that the scrollview is supposed to show. However, I am not able to. This is my code.
func setupMainView() {
// This is where the image view and other UIViews which are supposed to go in the contentview are set up
self.setupImagesView()
self.setupView1()
self.setupView2()
self.setupView3()
self.setupView4()
self.scrollView = UIScrollView()
self.scrollView.backgroundColor = UIColor.white
self.view.addSubview(self.scrollView)
self.automaticallyAdjustsScrollViewInsets = false
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
self.contentView = UIView()
self.scrollView.layer.masksToBounds = false
self.scrollView.backgroundColor = UIColor.white
self.scrollView.layer.borderWidth = 0
self.scrollView.layer.borderColor = UIColor.white.cgColor
self.view.addSubview(scrollView)
self.contentView.addSubview(imagesScrollView)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
self.contentView.addSubview(view3)
self.contentView.addSubview(view4)
self.scrollView.addSubview(contentView)
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
var scrollViewHeight:CGFloat = 0.0;
for _ in self.scrollView.subviews {
scrollViewHeight += view.frame.size.height
}
var newHeight = scrollViewHeight * 1.1 + offset + 100
scrollView.contentSize = CGSize(width:screenWidth, height:newHeight)
scrollView.reloadInputViews()
}
The views are getting loaded etc, but I am not manage the scroll. It somehow either too little or too much.
Now, I tried setting the height of contentSize to scrollViewHeight and double of that etc. What I notice is that there is no predictability of how much it will scroll. Change from 1.1 to 1.6 .. there is too much whitescreen below the views, change it to 1.1 or 1.2 it does not even scroll to the bottom.
Note, everything has been set up programmatically, without storyboard etc.
Also note that I need to support all IOS devices with version > 10.
Am a little lost here. What am I doing wrong?
This is a very old way of configuring a scroll view - you should be using auto-layout.
And, you're doing a number of things wrong...
First, we'll assume you are setting frames of the various subviews in code you haven't shown.
However, the code you have shown creates a scrollView and adds it to self.view -- but you never set the frame of the scroll view.
Also, this part of your code:
for _ in self.scrollView.subviews {
scrollViewHeight += view.frame.size.height
}
you've added several views as subviews of contentView, then added contentView as the only subview of scrollView.
And... you are trying to increment scrollViewHeight by the height of your root view instead of the height of the scrollView's subviews.
So, scrollViewHeight will only be the height of self.view.
What you probably want to do is sum the heights of contentView.subviews:
var contentViewHeight: CGFloat = 0
for v in contentView.subviews {
contentViewHeight += v.frame.height
}
contentView.frame.size.height = contentViewHeight
then set the scrollView's contentSize.height to the height of contentView's frame.
Here is a very, very basic example, using explicitly set frame sizes -- again, though, you should start using auto-layout:
class SimpleScrollViewController: UIViewController {
var imagesScrollView: UIView!
var view1: UIView!
var view2: UIView!
var view3: UIView!
var view4: UIView!
var contentView: UIView!
var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
setupMainView()
}
func setupMainView() {
// This is where the image view and other UIViews which are supposed to go in the contentview are set up
self.setupImagesView()
self.setupView1()
self.setupView2()
self.setupView3()
self.setupView4()
self.scrollView = UIScrollView()
// let's use a color other than white so we can see the frame of the scrollView
self.scrollView.backgroundColor = UIColor.cyan
self.view.addSubview(self.scrollView)
self.automaticallyAdjustsScrollViewInsets = false
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.lightGray
self.scrollView.layer.masksToBounds = false
self.scrollView.layer.borderWidth = 0
self.scrollView.layer.borderColor = UIColor.white.cgColor
self.view.addSubview(scrollView)
self.contentView.addSubview(imagesScrollView)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
self.contentView.addSubview(view3)
self.contentView.addSubview(view4)
self.scrollView.addSubview(contentView)
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
var contentViewHeight: CGFloat = 0
for v in contentView.subviews {
contentViewHeight += v.frame.height
}
contentView.frame.size.height = contentViewHeight
// don't know what you're doing here....
//scrollView.reloadInputViews()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// here is where you know the frame of self.view
// so, make the scroll view cover the entire view
scrollView.frame = view.frame
// now, make contentView width equal to scrollView width
contentView.frame.size.width = scrollView.frame.size.width
// set the scrollView's content size
scrollView.contentSize = contentView.frame.size
}
func setupImagesView() -> Void {
imagesScrollView = UIView()
imagesScrollView.backgroundColor = .red
imagesScrollView.frame = CGRect(0, 0, 300, 100)
}
func setupView1() -> Void {
view1 = UIView()
view1.backgroundColor = .green
view1.frame = CGRect(20, imagesScrollView.frame.maxY, 300, 200)
}
func setupView2() -> Void {
view2 = UIView()
view2.backgroundColor = .blue
view2.frame = CGRect(40, view1.frame.maxY, 300, 250)
}
func setupView3() -> Void {
view3 = UIView()
view3.backgroundColor = .yellow
view3.frame = CGRect(60, view2.frame.maxY, 200, 275)
}
func setupView4() -> Void {
view4 = UIView()
view4.backgroundColor = .orange
view4.frame = CGRect(80, view3.frame.maxY, 200, 100)
}
}
If I remember correctly you need to in order for scroll view to work you need to implement a couple of delegate methods. You also need a couple of properties set.
contentSize is one
and I think min and max size
see: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate
also see Stanford University's
Paul Hagarty Developing IOS 11 apps with swift episode 9 for loads of information on UIScrollView
https://www.youtube.com/watch?v=B281mrPUGjg
seek to about 31mins for the scroll view information.
This may help in the setup of UIScrollView programatically:
https://sheikhamais.medium.com/how-to-use-the-new-uiscrollview-programmatically-baf270ee9b4

How do I create, add and position a UIScrollView programmatically so that it fills the screen?

I want to create a simple view hierarchy in a Swift project that has a UIScrollView which should act as an expandable container to the subviews which are: UILabel, UITextView and a Button.
The catch here is that I am doing it all programmatically using visual format language and cannot do it in Interface Builder. My code below displays a scrollview, however it cannot scroll down to show the views which are below it and the views themselves are not sized correctly. It is static. Also the subviews are not expanding to fill up the screen.
I want to display a scrollview which is fullscreen size and also its subviews fill the screen horizontally with varying heights depending on what size I set. They currently only are around 200pt wide which is unusual.
viewDidLoad() {
view.addSubview(scrollView)
//This function is a convenience function which applies constraints
view.addConstraints(withFormat: "H:|[v0]|", toViews: scrollView)
view.addConstraints(withFormat: "V:|[v0]|", toViews: scrollView)
//Here I add the 3 subviews mentioned above
scrollView.addSubview(nativeText)
scrollView.addSubview(mnemonicDescription)
scrollView.addSubview(addButton)
//Here I apply constraints using format language
scrollView.addConstraints(withFormat: "H:|[v0]|", toViews: foreignText)
// Note that this should make foreignText expand the full width. It doesn't, its very small
//I continue to add subviews with horizontal and vertical constraints however they do not fill the container view as expected.
}
Here is a (fairly) simple example of adding 3 views to a Scroll View and using VFL to set the constraints.
override func viewDidLoad() {
super.viewDidLoad()
// create a scroll view with gray background (so we can see it)
let theScrollView = UIScrollView()
theScrollView.backgroundColor = .gray
// add it to the view
view.addSubview(theScrollView)
// add constraints so the scroll view fills the view
view.addConstraintsWithFormat("H:|[v0]|", views: theScrollView)
view.addConstraintsWithFormat("V:|[v0]|", views: theScrollView)
// create three UIViews - nativeText (red), mnemonicDescription (green), addButton (blue)
let nativeText = UIView()
nativeText.translatesAutoresizingMaskIntoConstraints = false
nativeText.backgroundColor = .red
let mnemonicDescription = UIView()
mnemonicDescription.translatesAutoresizingMaskIntoConstraints = false
mnemonicDescription.backgroundColor = .green
let addButton = UIView()
addButton.translatesAutoresizingMaskIntoConstraints = false
addButton.backgroundColor = .blue
// add those three views to the scroll view
theScrollView.addSubview(nativeText)
theScrollView.addSubview(mnemonicDescription)
theScrollView.addSubview(addButton)
// set horizontal / width constraints for the three views so they fill the scroll view
// "H:|[v0(==v1)]|" means "make the width of v0 equal to the width of v1, and pin to leading and trailing"
theScrollView.addConstraintsWithFormat("H:|[v0(==v1)]|", views: nativeText, theScrollView)
theScrollView.addConstraintsWithFormat("H:|[v0(==v1)]|", views: mnemonicDescription, theScrollView)
theScrollView.addConstraintsWithFormat("H:|[v0(==v1)]|", views: addButton, theScrollView)
// set the vertical / height constraints of the three views
// (==200) means "set the height to 200"
// "|" means "pin to edge"
// "-40-" means 40 points of space
// so the following 3 lines will put:
// nativeText (Red view) pinned to the top of scrollview, height of 200
theScrollView.addConstraintsWithFormat("V:|[v0(==200)]", views: nativeText)
// mnemonicDescription (Green view) pinned 40 space to Red view, height of 300
theScrollView.addConstraintsWithFormat("V:[v0]-40-[v1(==300)]", views: nativeText, mnemonicDescription)
// addButton (Blue view) pinned 40 space to Green view, height of 250, *and* pinned to bottom of scrollview
theScrollView.addConstraintsWithFormat("V:[v0]-40-[v1(==250)]|", views: mnemonicDescription, addButton)
// it could also be expressed in a single statement
// comment out the above three lines of code, and
// un-comment this line to see the same result
//theScrollView.addConstraintsWithFormat("V:|[v0(==200)]-40-[v1(==300)]-40-[v2(==250)]|", views: nativeText, mnemonicDescription, addButton)
// using those example heights and spacing comes to a total of 830,
// so it will scroll vertically a little bit on a iPhone 7+ (736 pts tall)
}
This is not a very difficult thing to achieve. If you will research carefully you can find the answers to all you queries here itself.
Anyways I am providing you with the detailed code for the thing you want to achieve. Hope this help you.
Simply add this code below your ViewController Class.
let scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
// Variables for setting the scrollView and Views inside scrollView
let phoneHeight: CGFloat = self.view.frame.size.height
let phoneWidth: CGFloat = self.view.frame.size.width
var yPosition: CGFloat = 0
var scrollViewHeight: CGFloat = 0
// Setting scrollView attributes
scrollView.frame.size.height = phoneHeight
scrollView.frame.size.width = phoneWidth
scrollView.frame.origin.x = 0
scrollView.frame.origin.y = 0
// Adding scrollView to the mainView
self.view.addSubview(scrollView)
// Creating subViews for the scrollView
let view1 = UIView()
let view2 = UIView()
let view3 = UIView()
let view4 = UIView()
let view5 = UIView()
// Adding the subVies in an array
var subViewArray: [UIView] = []
subViewArray.append(view1)
subViewArray.append(view2)
subViewArray.append(view3)
subViewArray.append(view4)
subViewArray.append(view5)
// Adding the subViews to the scrollView
for subView in subViewArray {
// Setting the atributes for subViews
// Fixed height of 200px and width adjusts according to the phoneSize
subView.frame.size.height = 200
subView.frame.size.width = phoneWidth
subView.frame.origin.x = 0
subView.frame.origin.y = yPosition
// Adding the background color to the subViews
subView.backgroundColor = UIColor.blue
// Changing the yPosition and scrollViewHeight and adding a 20px margin for each subview in scrollView
yPosition += 200 + 20
scrollViewHeight += 200 + 20
// Adding labels to the subviews
let label1 = UILabel()
label1.text = "Label Added"
label1.textAlignment = .center
label1.textColor = UIColor.white
label1.frame.size.height = 30
label1.frame.size.width = phoneWidth
label1.frame.origin.x = 0
label1.frame.origin.y = 10
subView.addSubview(label1)
self.scrollView.addSubview(subView)
scrollView.contentSize = CGSize(width: phoneWidth, height: scrollViewHeight)
}
}

Custom view - a subclass of uiview cannot be resized

It must be a simple thing but I couldn't find a clue,
I am having a imageview and trying a scribbling view which is a subclass of UIview on top of it to allow scribbling. All works fine except for resizing the scribbling view, it occupies the whole screen all the time I couldn't make it smaller.
class ScribbleView: UIView
{
let backgroundLayer1 = CAShapeLayer()
required init()
{
// super.init(frame: CGRect.zero)
super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
layer.addSublayer(backgroundLayer1)
layer.masksToBounds = true
}
class HermiteScribbleView: ScribbleView, Scribblable
{
required init() {
print ("calling parent")
super.init()
}
}
/** main code **/
let hermiteScribbleView = HermiteScribbleView()
hermiteScribbleView.backgroundColor = .red
// imgView.autoresizesSubviews = true
// hermiteScribbleView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
//once this is done, hermieScribbleView is always stretched to full width and height no matter what I try
imgView.addSubview(hermiteScribbleView)
What i guess from above information is that, you might be using the frame of UIImageView to set the frame of UIView. you need to set the size of UIView same as the size of UIImage.
imgImageView.image.size
and secondly set the centre of UIView same as centre of UIImageView.
Hope this will work.

iOS UIScrollView's width is different from the width set in constraint

I'm new to iOS auto layout. I want to implement a view pager using UIScrollView with paging enabled. I set the scrollView to (w, h) to (200, 65), but the width of scrollView is always 240 at runtime. As the result, the offset of contents, the label I added into the scrollView, do not match to the scrollView's width. Why are they different? How do I get the right width?
private let contents = ["This is the 1st message",
"This is the 2nd message",
"This is the 3rd message"]
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
print("scrollView.bounds = \(scrollView.bounds)")
print("scrollView.frame = \(scrollView.frame)")
var x: CGFloat = 0
for content in contents {
let label = UILabel(frame: CGRectMake(x, 0, 0, 0))
label.text = content
label.sizeToFit()
print("label.frame = \(label.frame)")
scrollView.addSubview(label)
x += scrollView.bounds.width
}
scrollView.contentSize = CGSizeMake(x, scrollView.bounds.height)
}
Output:
scrollView.bounds = (0.0, 0.0, 240.0, 128.0)
scrollView.frame = (80.0, 156.0, 240.0, 128.0)
label.frame = (0.0, 0.0, 178.0, 20.3333333333333)
label.frame = (240.0, 0.0, 185.666666666667, 20.3333333333333)
label.frame = (480.0, 0.0, 182.333333333333, 20.3333333333333)
Your problem here is that you are using a size class wCompact hRegular for this constraints, so if you have another size class (for example different orientations) you don't have these constraints available.
To set the constraints just for iPhone I suggest doing it programmatically:
if (IDevice.currentDevice().userInterfaceIdiom == .Phone) {
// Set constraints or a different UIVIew here
}
And if you have your app available for iPhone Only you can use the class size Any-Any avoiding the programmatically set.

Make Top image in UIScrollView enlarge on scroll view bounds?

I have a UIScroll view with a UIView inside as content view. The layout is:
self.scrollView = UIScrollView()
self.view.addSubview(self.scrollView)
self.scrollView.backgroundColor = UIColor.yellowColor()
// pin all edges to the edges of the superview (self.view)
self.scrollView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.view)
}
// create contentView
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.redColor()
self.scrollView.addSubview(self.contentView)
// pin the edges of the contentView to the scrollView
self.contentView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.scrollView)
}
In my contentView I have a UIImageView at the top and some UILabels below:
let imageView = UIImageView(...)
let labe1 = UILabel(...)
let labe2 = UILabel(...)
self.contentView.addSubView(imageView)
self.contentView.addSubView(labe1)
self.contentView.addSubView(labe2)
Here is how it looks like:
When I pull down the scroll view the scroll view bounces at the top. I want that the image enlarges when the scroll view bounces.
I did this with the UIScrollViewDelegate:
func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY: CGFloat = scrollView.contentOffset.y
if offsetY < -64 {
let progress:CGFloat = fabs(offsetY + 64) / 100
self.imageView.transform = CGAffineTransformMakeScale(1 + progress, 1 + progress)
}
}
This works but the problem is that when the image is transformed it should not overlay the border to the red area which contains the Labels. Here is what happens when I bounce:
I want that the image still enlarges but not overlapping the border to the red area. How can I do that?
I suggest you to resize the whole UIScrollView with pager view - don't forget to reset its's frame back to prevent wrong paging.
Just add an empty UIView with some background color as highlighted in image
Or in your code
let imageView = UIImageView(...)
let labe1 = UILabel(...)
let labe2 = UILabel(...)
self.contentView.addSubView(imageView)
//add one more UIView with some background color here
self.contentView.addSubView(labe1)
self.contentView.addSubView(labe2)

Resources