jplozano
jplozano

Reputation: 626

Refresh Control on top of tableview when I go back to view controller

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?

Upvotes: 7

Views: 13745

Answers (11)

Andrea
Andrea

Reputation: 19

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()
}

Upvotes: 0

Leon
Leon

Reputation: 3726

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

tableView.backgroundView = refreshControl

Upvotes: 4

Wagner De Paula
Wagner De Paula

Reputation: 31

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
}()

Upvotes: 0

M.Bonjour
M.Bonjour

Reputation: 1152

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.

Upvotes: 11

Dilip Jangid
Dilip Jangid

Reputation: 764

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 :-)

Upvotes: 0

Andrej Jaššo
Andrej Jaššo

Reputation: 1

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

Upvotes: 0

Vladimir Vlasov
Vladimir Vlasov

Reputation: 2090

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.

Upvotes: 0

Ankit Kumar Gupta
Ankit Kumar Gupta

Reputation: 4042

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)

Upvotes: 3

jplozano
jplozano

Reputation: 626

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.

Upvotes: 10

jandro_es
jandro_es

Reputation: 44

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

Upvotes: 0

Dima Deplov
Dima Deplov

Reputation: 3718

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.

Upvotes: 0

Related Questions