UITableView not executing to scrollViewDidScroll when load first time - ios

Im working on UITableview load more data from api.
-scrollViewDidScroll method is calling when we first time load screen but calling when we scroll or at end of screen.
I need this when i load the screen first time.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#"calledee %d",page);
if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height))
{
//mycode
}
}

Swift 4,5
private var firstLoad = true
// to avoid scrollViewDidScroll called when first time view controller load
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
firstLoad = false
}
// MARK: - UIScrollViewDelegate
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if firstLoad { return }
...
}
}

Related

Detect when a user swipes past a certain point in a UICollectionView's scrollViewWillBeginDragging?

In my collectionView the cells are the size of the entire screen, I use vertical scrolling, and I have .isPagingEnabled = true. I needed to prevent the user from swiping fast. Changing the .decelerationRate didn't work. The only thing that I found was to disable/enable the scrollView as the user swipes which almost works fine.
In the pic below, when the swiping starts, I disable the scrollView and show a red label at the top that says "cv Disabled". When swiping ends because the user lifted their finger, I enable the scrollView and remove the label. But when I swipe a hairline bit and quickly lift my finger from the cell, the red label never disappears and the scroll stays disabled. The orange arrow points to the next cell with a beige background that's about to be shown when I subtly swiped.
In the code below using both tries everything works fine when scrolling past a certain point occurs like full screen or a quarter of the screen. The issue is if I subtly swipe a hairline bit, the scrollView stays disabled because the methods myScrollView?.isUserInteractionEnabled = true are in never gets fired.
1st try:
var myScrollView: UIScrollView?
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if myScrollView == nil {
myScrollView = scrollView
}
myScrollView?.isUserInteractionEnabled = false
showRedDisabledLabel()
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
myScrollView?.isUserInteractionEnabled = true
removeRedDisabledLabel()
}
2nd try:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if myScrollView == nil {
myScrollView = scrollView
}
myScrollView?.isUserInteractionEnabled = false
showRedDisabledLabel()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
NSObject.cancelPreviousPerformRequests(withTarget: self)
perform(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation), with: nil, afterDelay: 0.3)
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
myScrollView?.isUserInteractionEnabled = true
removeRedDisabledLabel()
NSObject.cancelPreviousPerformRequests(withTarget: self)
}
This problem literally only occurs when I swipe that hairline bit and quickly lift my finger, every other time the code from both tries works perfectly. The question is in scrollViewWillBeginDragging, how can I detect when a user swipes past a certain point so that I can then set myScrollView?.isUserInteractionEnabled = false
I couldn't find a way to to determine how much a user swiped in scrollViewWillBeginDragging but I did find a workaround to the actual problem of hairline scrolling. I put myScrollView?.isUserInteractionEnabled and the redLabel into their own functions, combined this answer and this answer, created an instance property named isScrolling, and use didSet to determine if it's scrolling or not and call the functions from there:
// Properties
var myScrollView: UIScrollView?
var isScrolling = false {
didSet {
if isScrolling {
disableScrollView()
} else {
enableScrollView()
}
}
}
// My 2 Functions
func disableScrollView() {
myScrollView?.isUserInteractionEnabled = false
showRedDisabledLabel()
}
func enableScrollView() {
myScrollView?.isUserInteractionEnabled = true
removeRedDisabledLabel()
}
// UIScrollView Delegate Methods
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if myScrollView == nil {
myScrollView = scrollView
}
isScrolling = true
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
isScrolling = false
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
isScrolling = false
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
NSObject.cancelPreviousPerformRequests(withTarget: self)
perform(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation), with: nil, afterDelay: 0.3)
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
isScrolling = false
NSObject.cancelPreviousPerformRequests(withTarget: self)
}

How to remove scrolling inside iOS from the web view of swift5?

I am currently doing a webview. My problem is that I want to prevent iOS from scrolling inside. The scrolling I speak is not the scrolling created in the Web view, but the bouncing scrolling that occurs when you touch up or up by touching down.
It doesn't work for me.
override func loadView() {
super.loadView()
WKWebView.scrollView.bounces = false
URLCache.shared.removeAllCachedResponses()
URLCache.shared.diskCapacity = 0
URLCache.shared.memoryCapacity = 0
...
class WebViewController: ... UIScrollViewDelegate {
override func loadView() {
super.loadView()
mainWebView.scrollView.bounces = false
mainWebView.scrollView.delegate = self
...
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.frame.size.height) {
scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: scrollView.contentSize.height - scrollView.frame.size.height), animated: false)
}
}
I'm using WKWebView
Thanks you in advance.
This problem was where the function was executed. This function does not work when executed in the LoadView function. You must run it in the ViewDidLoad function.
override func viewDidLoad() {
super.viewDidLoad()
WKWebView.scrollView.bounces = false

how to know UIwebview is scrolling down side or up side? (swift)

in my app i am using small menu at the bottom of uiwebview. and i want to make like when user scroll downside that view must be hide. and when scrolling upside view must be unhide.
Like Safari.
this is what i tried
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
print("Going Down")
viewbottom.hidden = true
viewHieght.constant = 0
}
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
print("Going Up")
viewbottom.hidden = false
viewHieght.constant = 45
}
but by using this code its continuously showing up and down.
Use scroll view pan gesture recogniser to determine the direction.
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translationInView(scrollView.superview).y > 0 {
// scrolls down
} else {
// scrolls up
}
}
keep tracking the scrollView.contentOffset.y value and compare the last with the current value like so:
in your mainView add: var lastScrollOffset = CGFloat()
compare the last value with the current one in func scrollViewDidScroll(scrollView: UIScrollView)
func scrollViewDidScroll(scrollView: UIScrollView) {
let scrollOffset = scrollView.contentOffset.y
if lastScrollOffset < scrollOffset{
//scrolling down
}else if lastScrollOffset > scrollOffset {
//scrolling up
}else{
//going crazy
}
lastScrollOffset = scrollOffset
}

How can I refresh the table view but disable the bounce?

I want to disable my scrollview bounce when scroll down.
When I disable bounce vertically I can't refresh my table.
Any suggestion how to disable bounce, but enable refresh table?
I'm refreshing this way:
self.refreshControl = UIRefreshControl()
self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(refreshControl)
func refresh(sender:AnyObject) {
getJson()
self.tableView.reloadData()
self.refreshControl.endRefreshing()
}
Thanks.
Just did it this way:
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0.0 {
return
}
if (scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.frame.size.height) {
scrollView.setContentOffset(CGPointMake(scrollView.contentOffset.x, scrollView.contentSize.height - scrollView.frame.size.height), animated: false)
}
}
Implement the scrollViewDidScroll method in the UIScrollViewDelegate
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y > 0 {
scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: 0), animated: false)
}
}
}
This allows the table view to pull to refresh down, but restricts pulling up. This removes the bounce in the upwards direction.
Side-effect: this will not let you go beyond the cells already on view, so you can't scroll down to cells not visible.

Get scroll position of UIPageViewController

I am using a UIPageViewController, and I need to get the scroll position of the ViewController as the users swipe so I can partially fade some assets while the view is transitioning to the next UIViewController.
The delegate and datasource methods of UIPageViewController don't seem to provide any access to this, and internally I'm assuming that the UIPageViewController must be using a scroll view somewhere, but it doesn't seem to directly subclass it so I'm not able to call
func scrollViewDidScroll(scrollView: UIScrollView) {
}
I've seen some other posts suggestion to grab a reference to the pageViewController!.view.subviews and then the first index is a scrollView, but this seems very hacky. I'm wondering if there is a more standard way to handle this.
You can search for the UIScrollView inside your UIPageViewController. To do that, you will have to implement the UIScrollViewDelegate.
After that you can get your scrollView:
for v in pageViewController.view.subviews{
if v.isKindOfClass(UIScrollView){
(v as UIScrollView).delegate = self
}
}
After that, you are able to use all the UIScrollViewDelegate-methods and so you can override the scrollViewDidScroll method where you can get the scrollPosition:
func scrollViewDidScroll(scrollView: UIScrollView) {
//your Code
}
Or if you want a one-liner:
let scrollView = view.subviews.filter { $0 is UIScrollView }.first as! UIScrollView
scrollView.delegate = self
UIPageViewController scroll doesn't work like normal scrollview and you can't get scrollView.contentOffset like other scrollViews.
so here is a trick to get what's going on when user scrolls :
first you have to find scrollview and set delegate to current viewController like other answers said.
class YourViewController : UIPageViewController {
var startOffset = CGFloat(0) //define this
override func viewDidLoad() {
super.viewDidLoad()
//from other answers
for v in view.subviews{
if v is UIScrollView {
(v as! UIScrollView).delegate = self
}
}
}
.
.
.
}
extension YourViewController : UIScrollViewDelegate{
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
startOffset = scrollView.contentOffset.x
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
var direction = 0 //scroll stopped
if startOffset < scrollView.contentOffset.x {
direction = 1 //going right
}else if startOffset > scrollView.contentOffset.x {
direction = -1 //going left
}
let positionFromStartOfCurrentPage = abs(startOffset - scrollView.contentOffset.x)
let percent = positionFromStartOfCurrentPage / self.view.frame.width
//you can decide what to do with scroll
}
}
Similar to Christian's answer but a bit more Swift-like (and not unnecessarily continuing to loop through view.subviews):
for view in self.view.subviews {
if let view = view as? UIScrollView {
view.delegate = self
break
}
}
As of iOS 13, the UIPageViewController seems to reset the scrollview's contentOffset once it transitions to another view controller. Here is a working solution:
Find the child scrollView and set its delegate to self, as other answers suggested
Keep track of the current page index of the pageViewController:
var currentPageIndex = 0
// The pageViewController's viewControllers
let orderredViewControllers: [UIViewController] = [controller1, controller2, ...]
pageViewController.delegate = self
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard completed, let currentViewController = pageViewController.viewControllers?.first else { return }
currentPageIndex = orderredViewControllers.firstIndex(of: currentViewController)!
}
Get the progress that ranges from 0 to 1
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentOffsetX = scrollView.contentOffset.x
let width = scrollView.frame.size.width
let offset = CGFloat(currentPageIndex) / CGFloat(orderredViewControllers.count - 1)
let progress = (contentOffsetX - width) / width + offset
}
var pageViewController: PageViewController? {
didSet {
pageViewController?.dataSource = self
pageViewController?.delegate = self
scrollView?.delegate = self
}
}
lazy var scrollView: UIScrollView? = {
for subview in pageViewController?.view?.subviews ?? [] {
if let scrollView = subview as? UIScrollView {
return scrollView
}
}
return nil
}()
extension BaseFeedViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.x
let bounds = scrollView.bounds.width
let page = CGFloat(self.currentPage)
let count = CGFloat(viewControllers.count)
let percentage = (offset - bounds + page * bounds) / (count * bounds - bounds)
print(abs(percentage))
}
}
To make the code as readable and separated as possible, I would define an extension on UIPageViewController:
extension UIPageViewController {
var scrollView: UIScrollView? {
view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView
}
}
It's quite easy to set yourself as the delegate for scroll view events, as so:
pageViewController.scrollView?.delegate = self

Resources