Swift -how is a collectionView's scrollView.content.y determined? - ios

I have a collectionView with 5 cells. After I drag the collectionView down using this scrollView method I can get the following:
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let currentOffset = scrollView.contentOffset.y // 92 ???
print("currentOffset: \(currentOffset)")
let contentHeight = scrollView.contentSize.height // height of all the cells added together -616.310
print("contentHeight: \(contentHeight)")
let frame = scrollView.frame.size.height // height of the collectionView itself from it's topAnchor to it's bottomAnchor - 725
print("frame: \(frame)")
let maximumOffset = contentHeight - frame // -108.689453125 // distance between contentHeight (bottom of all the cells) and the frame
print("maximumOffset: \(maximumOffset)")
}
The contentHeight is the height of all the cells added together which is: -616.310
The frame is the height of the collectionView itself in between it's topAnchor and it's bottomAnchor which is: 725
The maximumOffset is the distance between contentHeight (bottom of all the cells) and the frame which is: -108.689453125
What I can't figure out is where does the currentOffset which is 92 comes from.
This isn't very clear:
contentOffset
The point at which the origin of the content view is offset from the
origin of the scroll view.
After dragging the collectionView, how is the currentOffset determined?
In the picture below at the bottom is the debugger with the print statements from the scrollViewDidEndDragging method
Not sure if this makes any difference or not but the collectionView's topAnchor and bottomAnchor are pinned to the view's safeAreaLayoutGuide ..., constant: 0 or it's anchored directly to it's top and bottom

It's how far the scrollView's top left hand corner (0,0) is from the top of the collectionView's top left hand corner (0,0) after the the collectionView is first dragged and let go.
So it was initially pulled up 92 points (0,-92) then let go.

Related

Change a label position while scrollview is scrolling

I have a label outside of a scrollview. I want to move the label from left to right of screen and vice versa when scrollview scrolling up and down.I wrote this code and it works when scrollview is scrolling in normal speed and when it scrolls very fast the label x position changes very slowly. How I can do that for all scrollview scrolling speeds?
func scrollViewDidScroll(_ scrollView: UIScrollView!) {
let offset = scrollView.contentOffset.y
let imahey = view.convert(placeImgview.frame, from:scrollview).origin.y + placeImgview.frame.width
print(offset)
let ratio: CGFloat = (-offset*1.0 / placeImgview.frame.height)
topBarView.alpha = -ratio
if placeName.frame.origin.x < -20 {
placeName.center = CGPoint(x: placeName.center.x - 3*ratio, y: placeName.center.y + ratio/1.5)
}
if (self.lastContentOffset > scrollView.contentOffset.y) {
placeName.center = CGPoint(x: placeName.center.x + 6*ratio, y: placeName.center.y - 2*ratio)
if offset == -20 {
placeName.frame.origin.x = -view.frame.width/2
placeName.frame.origin.y = 60
}
}
self.lastContentOffset = scrollView.contentOffset.y
}
So, the problem is that the faster you scroll, the larger the "gap" gets inbetween scrollViewDidScroll events.
You probably should consider to move around your label using UIView.animate... that would create a more consistent experience because the animation always has the same speed.
This way you could apply the animation using CGAffineTransform(translationX: , y: )
depending if your contentOffset.y passes a given threshold, whenever that label should appear or disappear.

Autoresizing UITableViewCell with UITextView which has text aligned to vertical center simultaneously?

Cell contains multiline text so I decided to use text view.
textView.textContainerInset = UIEdgeInsets.zero removes extra padding. Code to center it vertically:
extension UITextView {
func centerVertically() {
var topCorrect = (self.bounds.size.height - self.contentSize.height * self.zoomScale) / 2
topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
self.contentInset.top = topCorrect
}
}
This works if I predefine cell height in tableView(_:, heightForRowAt:).
But if I use UITableView.automaticDimension to resize the cell everything becomes broken.
How to solve this issue?
Use auto layout and pin leading, trailing, top and bottom, constraints of textview to the cell.
Disable scrolling in textview
Add a height constraint such that height is greater than or equal to a constant value (this is a min value that will show on the screen) for textview
User .automatic dimension
Try this and let me know if this worked out for you

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;

A bug of UIScrollView contentOffset?

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)

ScrollView with dynamic textview as subview

I want to fit a textView in a scrollView. As user types in the textView, the textView should expand in height. Everything seems to work fine until the textView reaches the height of the scrollView's height. The scrollView's contentSize is not updated. Here's my setup.
--scrollView
|--contentView
|--textView
The constraints are set as follow:
ScrollView - top, leading,trailing to the superview and bottom to bottomLayoutGuide
ContentView - top, leading, trailing, bottom to superview (ScrollView), align center x and align center y to superview (ScrollView)
TextView - top, leading, trailing to superview (ContentView), bottom is <=400 points to superview (ContentView)
In specific, I do not set a height for textView so it can resize dynamically as user types. I am using autolayout and textView is also set to scroll disabled.
I tried to force an update to the contentSize on TextViewDidChange delegate call.
func textViewDidChange(_ textView: UITextView) {
let size = CGSize(width: textView.frame.width, height: CGFloat(MAXFLOAT))
let value = textView.sizeThatFits(size)
let newHeight = value.height + textView.frame.minY
if newHeight > scrollView.contentSize.height {
contentView.frame = CGRect(x: 0, y: 0, width: textView.frame.width, height: newHeight)
scrollView.contentSize = CGSize(width: textView.frame.width, height: value.height + textView.frame.minY)
}
}
There's couple of issues with this approach. First will be the sizeToFit is called on every keystroke. And textView seems "slow down" the more text I typed to the textView.
Another issue is as soon a I rotate the screen, the scrolling of the scrollView stops working.
Can someone shed some light on this?
* update *
I was able to get it to a state where the scrollView recognize the dynamic height change of the textView.
My new constraints are like this:
ScrollView - top, leading,trailing to the superview and bottom to bottomLayoutGuide
ContentView - top, leading, trailing, bottom to superview (ScrollView), width to scrollView's superview
TextView - top, leading, trailing to superview (ContentView), bottom is =400 points to superview (ContentView)
Now this does update the contentSize accordingly and I don't need to calculate any frames. However this leads to another issue where the bottom constraint is ALWAYS 400. Ideally I only want the bottom constraint to be 400 on init. And as the textView expands, it should decrease to it reaches 0 (textView bottom reaches the bottom on the contentView)
Does anyone know how to go about this?

Resources