iOS swift: scrollview does not call didscrolltotop - ios

Evening: I have a scroll view with 3 views inside..
I have a problem with the scrollview delegates.
The didscrolltotop is never called, while the did scroll yes...
I can't understand the reason...
Any help?

Looks like it is only called under certain circumstances, the documentation talks about the scroll-to-top gesture meaning it may only work after a tap on the status bar and not basic scrolling. Also setting the scrollsToTop property to true seems to be required.
The scroll view sends this message when it finishes scrolling to the top of the content. It might call it immediately if the top of the content is already shown. For the scroll-to-top gesture (a tap on the status bar) to be effective, the scrollsToTop property of the UIScrollView must be set to YES.
You could also simply detect the top of the content using the contentOffset
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("The current Y offset is \(scrollView.contentOffset.y)")
if scrollView.contentOffset.y == 0 {
print("we are at the top")
}
}
You might also want to consider using the scrollViewDidEndDecelerating method for this as it will mean you only get one event after the scroll view has settled down.

Related

UITableView tap not working first time after constraints change

IMPORTANT: My problem is not that I'm implementing didDeelectRowAt instead of didSelectRowAt. Already checked that :)
I have a UITableView that is shown on part of the screen in a modally presented view controller. When the user is dragging it resizes to full screen and back to some defined min height. I'm doing this by implementing the following methods from the UIScrollViewDelegate:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard !scrollView.isDecelerating else { return }
let contentOffset = scrollView.contentOffset.y
if tableViewHeightConstraint.constant < view.frame.height && contentOffset > 0.0 {
tableViewHeightConstraint.constant = min(contentOffset + tableViewHeightConstraint.constant, view.frame.height)
scrollView.contentOffset.y = 0.0
return
}
if tableViewHeightConstraint.constant > minViewHeight && contentOffset < 0.0 {
tableViewHeightConstraint.constant = max(tableViewHeightConstraint.constant + contentOffset, minViewHeight)
scrollView.contentOffset.y = 0.0
}
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// Here I have some calculations if depending the dragging end position and the velocity the end size should be full screen or `minViewHeight`
// After calculating what the end size should be I'm animating the size change
heightConstraint.constant = newConstraintHeight
UIView.animate(withDuration: TimeInterval(calculatedAnimationDuration), delay: 0.0, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
Everything about the resizing and the scrolling works fine, but there is a problem that I cannot figure out why it's happening. It's the following:
When the view controller with the table view is shown for the first time with the min height and I tap on a cell it works fine.
If I drag to expand the table view to full screen height and tap on a cell, again it works fine.
If I drag to expand the table view to full screen height and then drag again to return it to the min height and then tap on a cell, nothing is happening, no UIScrollViewDelegate or UITableViewDelegate method is called at all. If I tap once more on a cell everything works fine.
One thing that I noticed is that after dragging the table view back to the min height the scroll indicator does not hide. On the first tap it hides, and on the second tap the didSelectRowAt is called.
UPDATE:
Here is a test repo for the problem: https://github.com/nikmin/DragTest
Please don't mind if the dragging doesn't work perfectly, I just put something so anyone can try it out, but I think the problem is easily reproducible.
Also one more thing... If you drag from full size all the way to the bottom so the table view reaches min height and you continue dragging so the content offset is < 0 and the you release, the problem is not happening.
Drag TableView to return it to the min height and then tap on a cell, nothing is happening because:
When you drag to expand the table view to full screen, scrollView.isDecelerating is true. So the code inside scrollViewDidScroll method will run.
But when you drag TableView to return it to the min height, scrollViewDidScroll is false. So the code inside scrollViewDidScroll method won't run. It's make the first tap do nothing.
Simply remove guard !scrollView.isDecelerating else { return } from scrollViewDidScroll. You will tap cell normally after drag TableView down.
But you need change logic a little, animation will go wrong after remove above line.
Hope it can help you ;)
After trying to figure out a solution to this without result, we (me and a UX designer) decided to change the behaviour a bit.
So in the real scenario in the app I'm implementing this in, the table view is inside another view that has also a title label and some other views above the table view. We decided to add a pan gesture recognizer to this root view and disable the scrolling of the table view when the view has the min size. This way the pan gesture recognizer will take over whenever the user tries to drag anywhere inside the view (including the table view), so the expanding of the view works. And the tap in the cell still works.
When the view has the max height the table view scroll is enabled so the user can scroll. The downside of this approach is that when the user scrolls to the top of the table view and continues scrolling the view will not decrease the size. But he still has the option to drag it down by dragging any of the views above the table view. When dragging down in this way, only the size of the table view changes, and the content offset isn't, which is the root of the problem (changing both at the same time).
It looks like incorrectly set content offset. The first touch cancels incorrect position(unscroll), this is why it is not registered. It might be better if we got an access to the full code to check it, because I can't tell you where exactly the problem lies, but I guess it is in method scrollViewDidScroll.

Twitter profile page iOS Swift dissection (Multiple UITableViews in UIScrollView)

hi... really how do they implement this? there are several tutorial for Twitter profile page. but they don't handle all possibilities...
first... when you scroll top or bottom any where, top view start scrolling until the segmented control, reach at top of the page...then scroll doesn't stop and subtable start scrolling until touching down and middle of way tableview start loading other rows dynamically ... so I don't think that they set content of scrollview statically
second things how do they handle sub tables... are they containerView?
if so, then the structure would be like this
ScrollView
TopView (User Info)
Segmented Controll
scrollView(to swipe right or left changing tables)
ContainerView For TWEETS
ContainerView For TWEETS & REPLIES
ContainerView For MEDIA
ContainerView For LIKES
am I right?
so how do they handle scrolls between sub tables and Top Scroll View to implementing topview position change base on scrolling...
it's mind blowing
this is How I Handel Nested ScrollViews...
i made a childDidScroll Protocol and my child tableviews implement that and in my profile page i can receive all child didscroll event then in
childDidScroll method :
//if child scrollview going up
if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0)
{
//check top scrollview if it is at bottom or top
//then disable the current scrollview
if mainScrollView.isAtBottom && scrollView.isAtTop{
scrollView.isScrollEnabled = false
}else{
//else enable scrolling for my childs
featuresVC.tableView!.isScrollEnabled = true
categoriesVC.tableView!.isScrollEnabled = true
shopsVC.tableView!.isScrollEnabled = true
}
print("up")
}
else
{
if mainScrollView.isAtTop {
scrollView.isScrollEnabled = false
mainScrollView.scrollToBottom()
}else{
featuresVC.tableView!.isScrollEnabled = true
categoriesVC.tableView!.isScrollEnabled = true
shopsVC.tableView!.isScrollEnabled = true
}
print("down")
}
but this solution has a some cons... and one of the is that first when child scrollview is at top or button, there should be two try to call my parent scrollview handle the scrolling, in first try i disable the child scrollview, and in second try parent scrollview handle the scrolling
** how can i say when you , my child, scrolling up, check if your parent is at top, then let him handle the scroll and when he touching the bottom, you can handle remain up scrolling, or tell the parent scrollview , if you are at top (user info is visible) if you or your child getting up scrolling, first you handle the scroll and when you rich at bottom(user info is not visible), let the remain scrolling on you child**
After a long long investigation that is how i achieve the twitter profile behaviour.
UnderlayScrollView
MasterScrollView
Header ViewController
Bottom ViewController
PagerTabItems [CollectionView]
UIPagerController or any other horizontal scroll (Parchment, XLPagerTabStrip).
UnderlayScrollView is responsible of controlling the scroll gesture. its contentoffset is used to adjust inner scroll contentOffsets. Contentsize of the underlaying scroll is same as the masterscroll's contentsize.
See the source code on github for more. click
I believe you are mostly right, except for the topmost scroll view.
In a recent app, I implemented something similar following this tutorial:
Basically, the trick is to have a class be the scrolling delegate of the bottom UITableViews, listen to the scrollViewDidScroll modifications, and modify the top inset of the UITableView and the TopView.
The structure I would use is like this:
Topview
ScrollView (horizontal scroll)
Segmented Control
ScrollView (horizontal, paging scroll)
UITableView
UITableView
UITableView
UITableView
You are totally right in it being mind blowing. Looks so simple.
I found a library,
https://github.com/maxep/MXSegmentedPager
Its totally works fine

How to trigger an animation when UIScrollView is scrolled to maxY? SWIFT

I have a scroll view and I want an animation to start when the scrollview is scrolled to its end and a little bit further (+75 px). How is that possible? I thought about an if-condition (if view.bounds.maxy >= 1075). But how can this condition or function be called when the user scrolls?
First, make sure your ViewController is a subclass of UIScrollViewDelegate. Next, you want to set your scrollView's delegate to self in the ViewDidLoad(). Then, you'll want to use scrollViewDidScroll() and handle things from there.
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y >= 75.0 {
// Your animation code goes HERE... //
}
}
If you want your scroll to go further than the actual visible content, consider adding the required distance (+75px in your case) to the contentSize. This will enable the content to scroll further.
As for the animation, UIScrollViewDelegate has multiple methods that can help you. You can keep a check like:
if scrollView.contentOffset.y >= scrollView.contentSize.height {
// Animation code
}

Tell if UITableView has scrolled to top

I tried this:
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
But it didn't fire when I scrolled the table view to the top.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
Does fire so the delegate isn't the problem.
In viewDidLoad I also set [myTbl setDoesScrollToTop:YES];
The scrollViewDidScrollToTop: method fires when the user clicks on the status bar and the scrollsToTop property is set to YES. From the docs:
The scroll view sends this message when it finishes scrolling to the top of the content. It might call it immediately if the top of the content is already shown. For the scroll-to-top gesture (a tap on the status bar) to be effective, the scrollsToTop property of the UIScrollView must be set to YES.
It does not fire if the user manually scrolls to the top. If you want to handle this case you will have to implement the scrollViewDidScroll: method and check to see whether the scroll is at the top yourself.
You can check this through the contentOffset property e.g.:
if (scrollView.contentOffset.y == 0) { // TOP }
When the table view goes under navigation bar and safe area layout guides are enabled, the following check can be done:
if (tableView.contentOffset.y + tableView.safeAreaInsets.top) == 0 { ... }
Bonus: Check for contentSize if you want to avoid getting 0 before the content is load:
if tableView.contentSize.height > 0 &&
((tableView.contentOffset.y + tableView.safeAreaInsets.top) == 0) { ... }

Prevent UIWebView to scroll when keyboard appears

I have a UIWebView presenting a page containing input fields ().
When the user touches the field to enter data, the UIWebView is scrolled up and left to make the field stay visible for the user. Well ok.
I don't mind it is scrolled up (because everything below my form is hidden by the keyboard) but I would like to prevent the view from being scrolled left.
Is there a way ( headers in the html page, or iOS code) to prevent this behavior ?
Thanks
not beautiful but effective:
If you don't want your webView to scroll at all, you can implement the UIWebView's UIScrollViewDelegate and set the contentOffset to 0 on scrollViewDidScroll
aWebView.scrollView.delegate = self
func scrollViewDidScroll(scrollView: UIScrollView) {
scrollView.contentOffset = CGPointMake(0, 0)
}
I don't know how to prevent the scrolling entirely, but if you don't mind a little jitter, you can add this handler to the input field:
onfocus="setTimeout('window.scroll(0,0)',100)"
First you will have to capture the display of a Keyboard using UIKeyboardWillShowNotification .
Then restrict the contentSize width of the UIScrollView (of the UIWebview) to screen width which will ensure that the screen doesnot scroll horizontally.

Resources