Just to be clear, I don't want to change the distance, I just want to know what it is as I'm doing some other UI stuff at the same time. Is it a fixed distance or a function of the height of the associated UITableView?
The pull distance seems to be very close to 50% of the tableView height.
FYI: You can't modify the pull distance of UIRefreshControl.
Check my other answer for an alternative.
if you want to change the distance you use a little trick.
you can make it right with some modification
lazy var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.tintColor = UIColor.red
return refreshControl
}()
//refreshControl.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
every PullToRefresh must have couple lines of code like this, that handleRefresh function, do whatever you need to refresh the page.
you just need to comment out addTarget line and add this function to your code
```
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < -80 { //change 80 to whatever you want
if !self.refreshControl.isRefreshing {
handleRefresh()
}
}
}
I wrote this code with the help of Ashkan Ghodrat's answer
Related
Update:
I belive it may not be possible given the folowing line in apples documentation:
When the user drags the top of the scrollable content area downward
Found here.
Let me know if there is a way to do this.
I am trying to make it so that when the user swipe left (the way you swipe up in many apps with tableViews to reload) in a collection view it will show a loading icon and reload data (the reload data part I can handle myself).
How can I detect this so I can call a reloadData() method?
Note: I am using a UICollectionView which only has one column and x rows. At the first cell if the user swipes left it should show a loading icon and reload data.
I am looking for a way to detect the slide left intended to reload.
What I have tried:
let refreshControl = UIRefreshControl()
override func viewDidLoad() {
super.viewDidLoad()
viewDidLoadMethods()
refreshControl.tintColor = .black
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
collectionView.addSubview(refreshControl)
collectionView.alwaysBounceHorizontal = true
But this only works vertically.
I solved this problem with the following, but I should note that there is no default fucntionality like there is for vertical refresh:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset
let inset = scrollView.contentInset
let y: CGFloat = offset.x - inset.left
let reload_distance: CGFloat = -80
if y < reload_distance{
shouldReload = true
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let _ = scrollView as? UICollectionView {
currentlyScrolling = false
if shouldReload {
baseVC.showReloading()
reloadCollectionView()
}
}
}
I have trouble hiding refreshControl once a user leaves the ViewController while refreshControl is still visible. I have tried setting it removing it from superView (tableView), replacing it with new one, etc... The issue still remains with tableView when user returns to the screen, top content insets remain from refreshControl before and it leaves a white space on top of tableView and if I do not replace/hide refreshControl, it will be visible at this point.
Any suggestions?
Image: Transition between screens, refreshControl does not hide on viewDidDisappear
Try this one :-
refreshControl.tintColor = .clear
Initialize the refresh control:
lazy var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action:
#selector(ViewController.handleRefresh(_:)),
for: UIControlEvents.valueChanged)
refreshControl.tintColor = UIColor.red
return refreshControl
}()
Handle the refresh and end the refreshing:
func handleRefresh(_ refreshControl: UIRefreshControl) {
self.tableView.reloadData()
refreshControl.endRefreshing()
}
Add the refresh control:
self.tableView.addSubview(self.refreshControl)
When you present the new screen just use .endRefreshing() on your refreshControl.
I have contacted a friend that gave a really nice answer. This was the code that helped me smoothly remove refreshControl in case it was stuck in frozen state on screen:
func forceHideRefreshControl(tableView: UITableView) {
if tableView.contentOffset.y < 0 { // Move tableView to top
tableView.setContentOffset(CGPoint.zero, animated: true)
}
}
Though if view controller hasn't finished loading, it won't have refreshControl visible. For that you'd need to call beginRefreshing() on it again, but I would do it with delay to avoid any animation problems. In any case, I think this was the best solution that actually removed the white spacing on top. I do not know why endRefreshing() did not work, but at least I found another way. Hope this helps anyone! :)
NOTE: However, this solution is tested on stuck/frozen refreshControl only. I do not know what effect it will have if you do not have this problem, but still use this solution for hiding refreshControl.
I have a view which contains another view on the top part, which I'm using to show some basic information. It has about 40% of the total view height. Below that "header" view, I'm using a UICollectionView which is scrollable. Now I've added a UIRefreshControl to my UICollectionView, but refreshing does never occur, because the user can't pull down the UICollectionView that far. When I reduce the height of the top view, it starts working because there's enough space to pull the collectionview down then.
Here's how I'm adding the refreshControl:
self.matchDetailRefreshControl = UIRefreshControl()
self.matchDetailRefreshControl.addTarget(self, action: #selector(MatchDetailViewController.fetchAll), forControlEvents: .ValueChanged)
self.collectionView!.addSubview(self.matchDetailRefreshControl)
self.collectionView!.alwaysBounceVertical = true
Have a look at this screenshot for reference:
As you can see, the UIRefreshControl doesn't get fully filled, while my finger is already at the bottom of the screen.
How can I fix that?
You can implement scrollViewDidScroll.
If the scrollView's contentOffset is past a certain point, then implement your refresh programmatically using beginRefreshing()
eg (with the refresh control connected to an outlet named 'refreshControl')
func scrollViewDidScroll(scrollView: UIScrollView) {
let currentOffset = scrollView.contentOffset
let yOffset = currentOffset.y
if yOffset < -30.0 && !refreshControl.refreshing {
refreshControl.beginRefreshing()
}
}
don't forget to set the scrollView's delegate to self if you haven't already
edit: sorry it's beginRefreshing(), not startRefreshing().
I have decided to use UIScrollView instead of UITableView in my app for many reasons. When I was using UITableView, I was able to add a UIRefreshControl with no issue whatsoever. When I use the same code on my UIScrollView however, nothing happens. I have tried several third-party refresher libraries, and none seem to work. Any help is appreciated.
Source:
var scroll: UIScrollView!
var refreshControl: UIRefreshControl!
override func viewDidLoad()
{
super.viewDidLoad()
self.scroll = UIScrollView(frame: self.view.frame)
self.scroll.contentSize = CGSize(width: self.view.bounds.width, height: 10)
self.refreshControl = UIRefreshControl()
self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
//self.refreshControl.addTarget(self, action: "loadPostsIntoCards", forControlEvents: UIControlEvents.ValueChanged)
self.scroll.addSubview(self.refreshControl)
self.view.addSubview(self.scroll)
//self.loadPostsIntoCards()
}
The loadPostsIntoCards method does my API call, and creates UIViews ("cards") that are added to the UIScrollView. I then change the contentSize of the UIScrollView based upon the total height of all the cards. The cards are added perfectly, and the scroll view acts as expected, except with regards to the UIRefreshControl.
Thanks in advance for any help!
EDIT:
After some fooling around I found another solution that will allow the scrollview to scroll (and trigger the refresh control) even when the contentSize is smaller than the scrollView height. Just set the alwaysBounceVertical property to true:
self.scroll.alwaysBounceVertical = true
OLD ANSWER:
Your content size should be larger than your scrollview's height in order to make this work. Change this line:
self.scroll.contentSize = CGSize(width: self.view.bounds.width, height: 10)
To:
self.scroll.contentSize = CGSize(width: self.view.bounds.width, height: self.view.bounds.height+1)
That would allow your RefreshControl to kick in.
Or, in your case you should probably calculate the height of your cards and then set that as the height for your contentSize.
In UIRefreshControl in iOS App, I think the default setting is that when I pull down UITableView(UISrollView) by about "100px", refresh begins.
I would like to make the above value smaller.(for example, "50px")
Is this possible?
If possible, please tell me sample codes.
Try this:
// definition
extension UIRefreshControl {
func refreshManually() {
beginRefreshing()
sendActions(for: .valueChanged)
}
}
// usage
var isRefreshingManually = false
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < -80.0 {
if !isRefreshingManually && !refreshControl.isRefreshing {
isRefreshingManually = true
refreshControl.refreshManually()
}
} else if scrollView.contentOffset.y >= 0 {
isRefreshingManually = false
}
}
My sample code is for UICollectionView, but UITableView & UIScollView work well, too.
Replace "-80.0" in my code to threshould value you want.
you can make it right with some modification
lazy var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.tintColor = UIColor.red
return refreshControl
}()
//refreshControl.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
every PullToRefresh must have couple lines of code like this, that handleRefresh function, do whatever you need to refresh the page.
you just need to comment out addTarget line and add this function to your code
```
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < -80 { //change 80 to whatever you want
if !self.refreshControl.isRefreshing {
handleRefresh()
}
}
}
You can try this method. When UIRefreshControl is added before UITableView(UIScrollView) is appear , the height of the view may be bigger than the actually height the view display on screen , so the UIRefreshControl pull to refresh range is bigger than actual
let ori = tableView.frame
let temp_frame = CGRect.init(x: ori.origin.x, y: ori.origin.y, width:
ori.size.width, height: ori.size.height/1.3 )
tableView.frame = temp_frame
tableView.addSubview(UIRefreshControl())
tableView.frame = ori
This code modified the height of view before adding refreshControl , you can adjust the height based on your preference.