7

For some reason, the first time the view controller is loaded, the refresh control behaves as expected (that it, showing under tableview when swipe down). The problem is when I tap on another tab, and then go back to the view controller, the refresh control does not fade in anymore and is visible on top of the tableview.

When I go back to view controller

This is part of the code:

class CoreTableViewController : UIViewController, UITableViewDataSource, UITableViewDelegate {

var tableView:UITableView!
var tableData:Array<AnyObject> = []
var dataFetched:Bool = false
var refreshControl:UIRefreshControl?

override func viewDidLoad() {
    super.viewDidLoad()

    self.edgesForExtendedLayout = UIRectEdge.None;

    self.tableView = self.assignTableView()
    self.tableView.delegate = self
    self.tableView.dataSource = self
    self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None;

    if self.enableRefresher() {
        self.setRefreshControl()
    }
}

func enableRefresher() -> Bool {
    return true
}

func fetchData(cleanFirst:Bool = false) {
    // Fetch data.
}

func refresh() {
    self.fetchData(true)
    self.refreshControl!.endRefreshing()
}

func setRefreshControl() {
    self.refreshControl = UIRefreshControl()
    //self.refreshControl!.attributedTitle = NSAttributedString(string: "Actualizar")
    self.refreshControl!.addTarget(self, action: #selector(CoreTableViewController.refresh), forControlEvents: UIControlEvents.ValueChanged)
    self.tableView.addSubview(refreshControl!)
}

}

Any thoughts?

jplozano
  • 606
  • 1
  • 7
  • 23

11 Answers11

11

Try to set z index as -1 which means UIRefreshControl is behind any view.

refreshControl.layer.zPosition = -1

and

tableView.refreshControl?.beginRefreshing()
self.tableView.refreshControl?.endRefreshing()

Both must use in pair.

Johnny
  • 1,112
  • 1
  • 13
  • 21
10

I found the fix here: https://stackoverflow.com/a/29088409/209200

Instead of self.tableView.addSubview(refreshControl!), it should be self.tableView.insertSubview(refreshControl!, atIndex: 0).

That was causing the refres control to appear over the tableview.

Community
  • 1
  • 1
jplozano
  • 606
  • 1
  • 7
  • 23
4

Inserting the refresh control at index 0 doesn't work, just set it as the tableview's background view:

tableView.backgroundView = refreshControl
Leon
  • 3,614
  • 1
  • 33
  • 46
3

Try this,

The Above solutions are fine but tableView.refreshControl is available for UITableViewController only, till iOS 9.x and comes in UITableView from iOS 10.x onwards.

To fix this Just use :

Written in Swift 3 -

let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(FeedViewController.loadNewData), for: UIControlEvents.valueChanged)
// Fix for the RefreshControl Not appearing in background
tableView?.addSubview(refreshControl)
tableView.sendSubview(toBack: refreshControl)
Ankit Kumar Gupta
  • 3,994
  • 4
  • 31
  • 54
0

As I examine your code and your question, I think the problem is here:

"In addition to assigning a refresh control to a table view controller’s refreshControl property, you must configure the target and action of the control itself." https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIRefreshControl_class/index.html#//apple_ref/occ/instm/UIRefreshControl/endRefreshing

So, from documentation, you need to use UITableViewController, not UIViewController. And set it's refreshControl property.

In the current state, you just add UIRefreshControl as regular subview.

Dima Deplov
  • 3,688
  • 7
  • 45
  • 77
  • What about if you want to use a collection view? I have exactly the same problem with the refresh control inside a collection view. – Ricardo May 04 '17 at 10:44
  • @Ricardo, check out collection view documentation. Looks like UIRefreshControl is made exclusively for UITableView. Maybe you need to browse for already implemented solution or made it by yourself... Anyway it's a topic for different question. – Dima Deplov May 04 '17 at 20:14
0

It looks ok, I suspect it's because some race condition regarding the creation of the control itself. I found out that using lazy properties helps a lot in this cases. I'll do something like this:

lazy var refreshControl: UIRefreshControl = {
    let refreshControl = UIRefreshControl()
    refreshControl.addTarget(self, action:#selector(CoreTableViewController.refresh(_:)), forControlEvents: .ValueChanged)
    return refreshControl
}()

override public func viewDidLoad() {
    super.viewDidLoad()
    if self.enableRefresher() {
        tableView.addSubview(refreshControl)
    }
    ........
}

func refresh(refreshControl: UIRefreshControl) {
    self.fetchData(true)
    refreshControl.endRefreshing()
}

You don't need to use a TableViewController, an UIViewController is enough and more often than not more flexible.

Hope it helps

jandro_es
  • 44
  • 2
0

Unfortunately, no one solution above has helped me.

In my case, a refresh control appears after reloading a table view when the table view is empty. Maybe if the table view has at least one row it simply hides the refresh control, I haven't checked it. Also, when my view controller appears again it works like a workaround and it hides the refresh control.

But I've found another workaround to hide the refresh control immediately. An async call is mandatory here.

tableView.reloadData()
DispatchQueue.main.async {
  refreshControl.endRefreshing()
}

I was inspired by this answer in another post.

Vladimir Vlasov
  • 1,860
  • 3
  • 25
  • 38
0

to me worked using insertion at 0th index of table view and intializing like lazy var:

  private lazy var refreshControl: UIRefreshControl = {
        let refreshControl = UIRefreshControl()
        refreshControl.tintColor = .gray
        refreshControl.addTarget(self, action: #selector(refreshFollowingList(_:)), for: .valueChanged)
        return refreshControl
    }()

    func setupRefresher() {
        self.tableView.insertSubview(refreshControl, at: 0)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        setupRefresher()
    }

In case I just assigned to the tableviews refresh control the lazy var it was jumping a at the 109 pixel of content offset by 20 pixels so I fixed it like this. I am using a UIViewController not a UITableViewController, hope this helps

0

We have to do a simple configuration to assign UIRefreshControl to tableView as given below.

@IBOutlet weak var tableView : UITableView! {
    didSet {
        tableView.dataSource = self
        tableView.delegate = self
        tableView.refreshControl = UIRefreshControl()
        tableView.refreshControl?.addTarget(self, action: #selector(handleRefresh(_:)), for: UIControl.Event.valueChanged)

    }
}

Now add the handleRefresh method to handle your logic

@objc
func handleRefresh(_ refreshControl: UIRefreshControl) {
    // Here i am disabling the user interaction while refreshing
    tableView.alpha = 0.5        
    self.view.isUserInteractionEnabled = false
    // Get your work done here

    // At the end stop the refresh control and enable user interaction again.      
    tableView.refreshControl?.endRefreshing()
    tableView.alpha = 1.0        
    self.view.isUserInteractionEnabled = true
}

Now when you come back from another tab, the refresh control stop spinning. So to handle that add the below lines of code...

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // Here we are forcefully end refreshing and again starting, as we know it works when we scroll down the table. We are doing it programmatically.
    if tableView.refreshControl!.isRefreshing {
        let contentOffset = tableView.contentOffset
        tableView.refreshControl?.endRefreshing()
        tableView.refreshControl?.beginRefreshing()
        tableView.contentOffset = contentOffset
    }
} 

Hope it will help the peoples :-)

Dilip Jangid
  • 754
  • 1
  • 10
  • 20
0

UIRefreshControl's container view is similar to a UIView, you should set isOpaque = true and clipToBounds = true as well.

private lazy var refreshControl: UIRefreshControl = {
    let refreshControl = UIRefreshControl(frame: .zero)
    refreshControl.isOpaque = true
    refreshControl.clipsToBounds = true
    refreshControl.backgroundColor = .black
    refreshControl.addTarget(self, action: #selector(self.refresh(_:)), for: .valueChanged)
    return refreshControl
}()
0

This is a simple solution:

tableView.refreshControl = UIRefreshControl()
tableView.refreshControl!.addTarget(self, action: #selector(self.refresh(_:)), for: .valueChanged)


@objc func refresh(_ sender: AnyObject) {
    // Action to take
    tableView.refreshControl!.endRefreshing()
}
Andrea
  • 19
  • 11