A bug of UIScrollView contentOffset? - ios

I set the property isPagingEnabled of a scrollView to true, like this:
let scrollView = UIScrollView(frame: view.bounds)
scrollView.contentSize = CGSize(width: view.bounds.width * 2, height: 0)
scrollView.delegate = self
scrollView.isPagingEnabled = true
view.addSubview(scrollView)
and delegate method:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print(scrollView.contentOffset.x)
}
When scrolling the view to the boundary, the console print, as shown in:
and
The values in the red box should not be printed out, right?
But, if setting isPagingEnabled to false, the console print is normal. Is this a bug of UIScrollView?

Your Content offset is showing wrong because your are using content size in viewdidload use content size in this method than print your content off set
override func viewDidLayoutSubviews()
{
scrollView.contentSize = CGSize(width: view.bounds.width * 2, height: 0)
}

As far as i Know, Its working correctly, when you are setting contentSize to your scrollview, that will respond to their delegate at first time itself.
func scrollViewDidScroll(_ scrollView: UIScrollView)
Whenever user try to scroll or drag, the above delegate will call recursively, Here your contentSize of width is double of Your view bounds width(so there is two page, the contentOffset.x will start with 0 and current position of scroll, if it is in second page, that will give your view width * n ), and you set isPagingEnabled to true.
Check this https://developer.apple.com/reference/uikit/uiscrollview/1619404-contentoffset
The default value is CGPoint​Zero.
Actually contentOffset will tell you that current scroll position.

Whenever UIScrollView make scrolling, this method scrollViewDidScroll(_ scrollView: UIScrollView) invokes. Any of the direction if you scroll you will get these types of output.
However if you want like a pagination approach then instead of this method you can use:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
print("pageNumber = \(pageNumber)")
}
By this method you will get the exact page no of UIScrollView.

Please use frame instead of bounds .
The bounds of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to its own coordinate system (0,0).The frame of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to the superview it is contained within.
let scrollView = UIScrollView(frame: view.frame.size)
scrollView.contentSize = CGSize(width: view.frame.size.width * 2,
height: 0)
scrollView.delegate = self
scrollView.isPagingEnabled = true
view.addSubview(scrollView)

Related

How to make UIView automatically scroll with screen when NavBar scrolls past it?

How can I make a certain element on my screen scroll with the screen (e.i. have a fixed position relative to the screen) when my navbar scrolls past it?
In other words, I have an element that isn't at the top of my screen and you need to scroll down to its position before it starts scrolling with the screen. Then, when you scroll back up to the top of the screen, the item goes back to its original position and stays there.
I am currently using a UIScrollView to scroll across my screen. My original plan was to get certain dimensions and basically do something like this when a isScrolling() method is called (pseudocode):
if scrolledPositionOnScreen > positionOfUIView {
scrollUIViewWithRestOfScreen()
} else {
putUIViewInOriginalPosition()
}
However, this doesn't work well when I use different size screens even though I am getting these variables dynamically with respect to screen size.
Is there a delegate or something that will do what I am trying to do/is there an easier, more consistent way?
Thanks!
Yes, there is the UIScrollViewDelegate delegate from which you can use the scrollViewDidScroll method:
class ViewController : UIViewController, UIScrollViewDelegate {
let scrollView = UIScrollView()
let frame = CGRect(origin: .init(x: 0, y: 100), size: .init(width: 200, height: 200))
lazy var v = UIView(frame: frame)
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.delegate = self
self.view.addSubview(scrollView)
self.scrollView.contentSize = CGSize(width: self.view.bounds.width, height: UIScreen.main.bounds.height + 1000)
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.addSubview(v)
self.v.backgroundColor = .red
NSLayoutConstraint.activate([
self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor),
self.scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
self.scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y > frame.origin.y {
v.center.y = scrollView.contentOffset.y + (v.bounds.height/2)
}
}
}

Define correctly height for UIScrollView

I'm creating a application using Swift3, and i have difficult to define correctly height for UIScrollView, i'm using autolayout and create this structure:
UIScrollView
UIView // The container view
UIImageView // Constraint Top Edges = 20 in relation to UIView
UITextView // Constraint Top Edges = 40 in relation to UIImageView
UITextView // Constraint Top Edges = 20 in relation to UITextView
UIButton // Constraint Top Edges 30 in relation to UITextView
Currently, i'm using this logic to calculate UIScrollView height
override func viewDidLayoutSubviews() {
var scrollHeight : CGFloat = 0.0
for view in self.containerView.subviews {
view.layoutIfNeeded()
scrollHeight += view.frame.size.height
}
// Adding height for scrollview, according to the sum of all child views
self.scrollView.contentSize.height = scrollHeight
}
But, i can only get the views height, and them not consider the Constraints "margins", i would like know any way to calculate correct height for UIScrollView, adjusted according their content.
Close! Let's add the top margin for each view:
override func viewDidLayoutSubviews() {
var scrollHeight : CGFloat = 0.0
for view in self.containerView.subviews {
view.layoutIfNeeded()
scrollHeight += view.frame.size.height
//Add the margins as well to the scrollHeight
scrollHeight += view.layoutMargins.top
}
// Adding height for scrollview, according to the sum of all child views
self.scrollView.contentSize = CGRect(x: self.scrollView.contentSize.width, y: scrollHeight)
}
self.scrollview.contentsize.height = containerview.height;

How to prevent scrolling left to right of collection view in horizontal scroll direction?

I made a UICollectionView with a horizontal scroll.
I want to scroll only one direction i.e right to left my cell view size is as full view. once I scrolling cell, it should not scroll left to right.
Please Try this,
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let row = scrollView.contentOffset.x / cellWidth
currentIndexShown = Int(row)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x < cellWidth * CGFloat(currentIndexShown){
scrollView.contentOffset = CGPoint(x: cellWidth * CGFloat(currentIndexShown), y: -20)
scrollView.bounces = false
} else {
scrollView.bounces = true
}
}

Why is UIView moved to right on offsetBy

I have a scrollview with an UIImageView on it. When the user scrolls, I would like to keep the UIImageView at its original place. I am using offsetBy for this:
func scrollViewDidScroll(scrollView: UIScrollView) {
let offset = scrollView.contentOffset.y
self.headerView!.currentLoadLabel.frame = currentLoadLabelFrame.offsetBy(dx: 0, dy: offset)
}
The UIImageView stays at his y position but moves to the farmost x position at the right of the screen. Why?
try following code:
self.headerView!.currentLoadLabel.frame = currentLoadLabelFrame.offsetBy(dx: scrollView.contentOffset.x, dy: scrollView.contentOffset.y)

Disabling Horizontal Scrolling from UIScrollView Swift

Scroll View
I have a UIScrollView, with constraints left: 0, top: 0, right: 0, bottom: 0
Inside Scroll View
At the top of this UIScrollView is a UIImageView with constraints left: 0, top: 0, right: 0, height: 200
Underneath this I have a UITextView with constraints left: 0, top: 0, right: 0, bottom: 0
This means the UITextView will resize with respect to its content, and I set the scrollingEnabled to false for the UITextView.
So, when I run, it almost works perfectly.
The one problem is the UIImageView takes up about 10% more than the actual screen width. Hence, horizontal scrolling is enabled.
I have tried adding the lines
imageView.frame = CGRect(0, 0, screenSize.width, 200)
scrlView.contentSize.width = screenSize.width
but this makes no difference. I can still scroll horizontally and the Image View still takes up around 10% more than the actual screen width.
Note, I have not set imageView screen width in storyboard, only programatically.
Any ideas?
Like this,
Swift 4.0
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.x>0 {
scrollView.contentOffset.x = 0
}
}
And, you can set this property:
scrollImg.isDirectionalLockEnabled = true
Swift 4
Horizontal Scroll Lock
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x != 0 {
scrollView.contentOffset.x = 0
}
}
You can change the x to y for vertical scrolling.
Make sure to add UIScrollViewDelegate like this:
class MyViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
...
}
And set the delegate for the ScrollView
scrollView.delegate = self
I changed this so that it just returns 0. No need to check at all if you want scroll off.
func scrollViewDidScroll(scrollView: UIScrollView) {
scrollView.contentOffset.x = 0
}
No need for the directional lock.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentOffset.x = 0
}
This stops the scrollview from scrolling towards the leading edge too.
If you are using storyboard and autolayout, then you should consider how ScrollView work in Storyboard with autolayout.
Consider the following.
Add a single view on your scrollView with constrains
left, right, top, bottom, height, width,
Make the outlet of the width and height
Add your subViews to the view you added in the scrollView
update the width to the screenSize eg: 320 for iPhone5 or 4.
self.viewWidth = SCREEN_WIDTH;
[self.view updateConstraints];
You can use
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentOffset.x = 0
}
Do not use scrollView.contentOffset.x < 0 or > 0 like others have mentioned because it will only stop scroll to one side horizontally and not both.

Resources