others-how to solve Cannot override mutable property 'refreshControl' of type 'UIRefreshControl?' with covariant type 'UIRefreshControl' when trying to add refresh control to your tableviewcontroller ?
1. Purpose
In this post, I would demo how to solve the below error when trying to add customized refresh control to UITableViewController with swift in iOS developement:
When we add this line to the class:
class MyTableViewController: UITableViewController {
lazy var refreshControl = UIRefreshControl()
....
}
We got this error from xcode
Cannot override mutable property 'refreshControl' of type 'UIRefreshControl?' with covariant type 'UIRefreshControl'
2. Environment
xcode 12
swift 5
3. The reason
When we add our own refreshControll
to our viewcontroller, it conflicts with the original refreshControl in UITableViewController. If we want to define another control, then change the name… refreshControl is a UIScrollView/UITableViewController internal variable since iOS10/iOS6 [respectively]. We’re trying to redefine it and that is what causing the error.
Or we can just redefine the variable like this:
self.refreshControl = UIRefreshControl()
You can find that the field refreshControl
is already in of UITableViewController :
4. The Solution
We can do this job as follows:
class MyTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl = UIRefreshControl() //key point
if let refreshControl = self.refreshControl {
refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl.addTarget(self, action: "refresh:", for: .valueChanged)
tableView.addSubview(refreshControl)
}
}
@objc func refresh(_ sender: AnyObject) {
print("refrehed")
DispatchQueue.global().async {
sleep(2)
DispatchQueue.main.async {
self.refreshControl?.endRefreshing()
}
}
}
....
}
In the above code, we did the following things:
- We created our own refreshControl to replace the inner refresh control of UITableViewController:
self.refreshControl = UIRefreshControl() //key point
- We add our customized Refresh Control to the UITableViewController, and set its attributes like text and action responder:
if let refreshControl = self.refreshControl {
refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl.addTarget(self, action: "refresh:", for: .valueChanged)
tableView.addSubview(refreshControl)
}
- We define a function to action to the RefreshControl
@objc func refresh(_ sender: AnyObject)
- At last we simuate the long running process , run it in the seperate thread by using DispatchQueue.global()
@objc func refresh(_ sender: AnyObject) {
print("refrehed")
DispatchQueue.global().async {
sleep(2)
DispatchQueue.main.async {
self.refreshControl?.endRefreshing()
}
}
}
In the above code, we use the DispathQueue to run the heavy-load task, what is a DispatchQueue?
A DispatchQueue is an abstraction layer on top of the GCD queue that allows you to perform tasks asynchronously and concurrently in your application. Tasks are always executed in the order they’re added to the queue.
Then we sleeped for 2 seconds in the seperate thread, and used the DispatchQueue.main.async
to update the UI.
DispatchQueue. main (the main queue) is a serial queue. sync and async do not determine serialization or currency of a queue, but instead refer to how the task is handled. … Asynchronous function returns control on the current queue right after task has been sent to be performed on the different queue
To summarize, we can run heavy-loading tasks in DispatchQueue.global(), and then update the UI in DispatchQueue.main.
Now it works!
4. Summary
In this post, I demonstrated how to solve the conflit of adding customized refresh control to UITableViewController.