Search code examples
iosswiftpull-to-refresh

How to pull to refresh without tableView?


I have next controller:

final class CategoryBreakdownViewController: UIViewController {
  
    private let scrollView = UIScrollView(alwaysBounceVertical: true)
    var refreshControl = UIRefreshControl()

Where in viewDidLoad I'm trying to implement pull to refresh without tableView:

 private func setupRefreshControl() {    
        scrollView.alwaysBounceVertical = true
        scrollView.bounces  = true
        refreshControl.addTarget(self, action: #selector(updateView), for: .valueChanged)
        self.scrollView.addSubview(refreshControl)
    }
    
    @objc func updateView() {
        SyncScheduler.syncImmediately(
            success: {
                self.refreshControl.endRefreshing()
            },
            failure: { [weak self] errorMessage in
                self?.present(message: errorMessage, style: .error)
                self?.refreshControl.endRefreshing()
            }
        )
    }

But in my case this solution doesn't worked, how to solve this problem?


Solution

  • There doesn't appear to be anything wrong with your approach.

    Here is a complete example -- it adds a "full view" scrollView, with a 200-pt tall red subview (so we can see the effect).

    When you drag down, you should see the UIRefreshControl "spinner" appear. Drag down far enough, and it will call the updateView() func. Since we don't have your SyncScheduler code, I added a 2-second async delay to simulate the process:

    final class CategoryBreakdownViewController: UIViewController {
        
        private let scrollView = UIScrollView()
        var refreshControl = UIRefreshControl()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            scrollView.backgroundColor = .systemTeal
            scrollView.translatesAutoresizingMaskIntoConstraints = false
    
            let testView = UIView()
            testView.backgroundColor = .red
            testView.translatesAutoresizingMaskIntoConstraints = false
    
            scrollView.addSubview(testView)
            view.addSubview(scrollView)
        
            let contentGuide = scrollView.contentLayoutGuide
            let frameGuide = scrollView.frameLayoutGuide
            
            NSLayoutConstraint.activate([
                scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
                scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
                scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
                scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
                
                testView.topAnchor.constraint(equalTo: contentGuide.topAnchor, constant: 20.0),
                testView.leadingAnchor.constraint(equalTo: contentGuide.leadingAnchor, constant: 20.0),
                testView.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor, constant: -20.0),
                testView.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor, constant: -20.0),
    
                testView.widthAnchor.constraint(equalTo: frameGuide.widthAnchor, constant: -40.0),
                testView.heightAnchor.constraint(equalToConstant: 200.0),
            ])
    
            setupRefreshControl()
        }
        
        private func setupRefreshControl() {
            scrollView.alwaysBounceVertical = true
            scrollView.bounces = true
            refreshControl.addTarget(self, action: #selector(updateView), for: .valueChanged)
            self.scrollView.addSubview(refreshControl)
        }
        
        @objc func updateView() {
    
            // simulate a background process that takes 2 seconds
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
                self.refreshControl.endRefreshing()
            })
            
    //      SyncScheduler.syncImmediately(
    //          success: {
    //              self.refreshControl.endRefreshing()
    //          },
    //          failure: { [weak self] errorMessage in
    //              self?.present(message: errorMessage, style: .error)
    //              self?.refreshControl.endRefreshing()
    //          }
    //      )
            
        }
    }