I have programmatically added a UIRefreshControl as a subview to my UICollectionView.
lazy var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(MyPhotospotsViewController.handleRefresh(_:)), for: UIControlEvents.valueChanged)
refreshControl.tintColor = UIColor.lightGray
return refreshControl
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.addSubview(refreshControl)
}
I want to move it down slightly but nothing I have done so far has worked. I tried the following.
refreshControl.frame = CGRect(x: refreshControl.frame.origin.x,
y: refreshControl.frame.origin.y - 50,
width: refreshControl.frame.size.width,
height: refreshControl.frame.size.height)
I've also had a look on google but all the answers are in Objective C.
I was thinking of programmatically adding constraints, but that feels incorrect.
You should use autolayout constraints for it or update its frame in -viewDidLayoutSubviews.
Related
I am new in IoS programming. I am involved in an existing project, and I am facing a problem.
UIRefreshControl is blocking views below. Like image attached (UIRefreshControl is transparent black).
UIRefreshControl is blocking views below it. But when I am doing some little scroll, the UIRefreshControl is disappeared, and then the button below is clickable.
Here is some snippet of the code.
CommentView.swift
lazy var collectionView: UICollectionView = {
layout.scrollDirection = .vertical
let collectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.isUserInteractionEnabled = true
collectionView.backgroundColor = .white
collectionView.refreshControl = refreshControl
collectionView.registerCustomCell(CommentCell.self)
collectionView.register(CommentHeaderView.self, forSupplementaryViewOfKind: CommentView.className, withReuseIdentifier: CommentHeaderView.className)
return collectionView
}()
lazy var refreshControl: UIRefreshControl = {
let refresh = UIRefreshControl()
refresh.tintColor = .primary
refresh.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
refresh.backgroundColor = UIColor.blackTransparent
return refresh
}()
The blocked views are declared inside CommentHeaderView.swift.
EDIT
I have tried
refresh.layer.zPosition = -100
but still not working.
Anyone can help me? Thanks in advance!
I tried solution below, and it works perfectly.
refreshControl.layer.zPosition = -1;
refreshControl.userInteractionEnabled = NO;
UIRefreshController goes over the UICollectionView
Thanks all!
I have multiple UICollectionViews all placed on a UIScrollView. I want to pull down on the UIScrollView (similar to Instagram and Twitter feed refreshers) and refresh the data in the UICollectionViews. However, what I tried hasn't worked.
Below is the code I tried implementing. When I run this and try to pull down on the UIScrollView. I am shown the dial that indicates refreshing, but nothing changes and "refresh" is never printed.
var refreshControl: UIRefreshControl!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.alwaysBounceVertical = true
scrollView.bounces = true
refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(didPullToRefresh), for: .valueChanged)
self.scrollView.addSubview(refreshControl)
}
#objc func didPullToRefresh() {
targetCollectionView.reloaddata()
print("Refersh")
// For End refrshing
refreshControl.endRefreshing()
}
Since there are multiple collection views inside scrollview, it may possible that the pull to refresh is not triggering on scrollview,(might be on collection view as multiple collection view are there)
Possible Reason Why didPullToRefresh method is not calling?
You have added the UIRefreshControl on scrollview. So when you are pulling down the view it is the collection view (not the scrollview, I think). That is the reason "refresh" is never printed.
How to detect which view pulled for refresh?
lets say you have 2 collection view and 1 scrollview on you self.view. so add UIRefreshControl on all the views like below...
//add refresh control on collection view 1
let refreshController1 = UIRefreshControl(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
refreshController1.addTarget(self, action: #selector(colView1Refreshed), for: .valueChanged)
collectionView1.refreshControl = refreshController1
//add refresh control on collection view 2
let refreshController2 = UIRefreshControl(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
refreshController2.addTarget(self, action: #selector(colView2Refreshed), for: .valueChanged)
collectionView2.refreshControl = refreshController2
//add refresh control on scrollview
let refreshController3 = UIRefreshControl(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
refreshController3.addTarget(self, action: #selector(scrollViewRefreshed), for: .valueChanged)
scrollView.refreshControl = refreshController3
And then implement these methods to check which view is pulled.
#objc func colView1Refreshed() {
print("collection view 1 pulled")
}
#objc func colView2Refreshed() {
print("collection view 2 pulled")
}
#objc func scrollViewRefreshed() {
print("collection view 3 pulled")
}
In this way you can identified which refresh control's action is triggred.
NOTE: The above mention code is just an example. If you have more than 2 collection view then you need to add UIRefreshControl on all the other views to detect it.
Currently there is a UITableViewContoller with many sections and rows.
What is the best way to add a button which floats on top of the table view. This button should not scroll when the cells are scrolled.
Currently I have the following code and with this the button still scrolls:
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(viewForFooter)
}
the viewForFooter is a separate view which contains the button which needs to be floating. Thank you. Any help would be greatly appreciated.
Here is an option if you really don't want to use a UIView with subviews...
override func viewDidLoad() {
super.viewDidLoad()
if let app = UIApplication.shared.delegate as? AppDelegate, let window = app.window {
print("adding view-with-button to keyWindow")
window.addSubview(viewForFooter)
viewForFooter.topAnchor.constraint(equalTo: window.topAnchor, constant: 120).isActive = true
viewForFooter.leftAnchor.constraint(equalTo: window.leftAnchor, constant: 40).isActive = true
}
// other stuff...
}
This will add the view as a subview of the "keyWindow" at 40,120, and will "hover" there while you scroll the table. I am assuming your viewForFooter is properly instantiated and you have the necessary constraints set up correctly.
The easiest way is to use a viewController that will contain the tableView as a subView and then You can add you floating button as a subview of the viewController
override func viewWillAppear(_ animated: Bool) {
self.btnPickup = UIButton()
self.btnPickup?.frame = CGRect(x: self.view.frame.size.width - 75, y: self.view.frame.size.height - 150, width: 50, height: 50)
// self.btnPickup?.setTitle("+", for: .normal)
self.btnPickup?.setBackgroundImage(#imageLiteral(resourceName: "add_user"), for: .normal)
self.btnPickup?.titleLabel?.textAlignment = .center
self.btnPickup?.titleLabel?.font = UIFont(name: (self.btnPickup?.titleLabel?.font.fontName)!, size: 50)
// self.btnPickup?.layer.borderColor = UIColor.black.cgColor
self.btnPickup?.backgroundColor = UIColor.white
// self.btnPickup?.layer.borderWidth = 1
self.btnPickup?.layer.cornerRadius = 25
self.btnPickup?.clipsToBounds = true
// self.btnPickup?.setTitleColor(UIColor.white, for: .normal)
self.btnPickup?.addTarget(self, action: #selector(DirectoryViewController.btnTapped(sender:)), for: .touchUpInside)
print(self.navigationController?.view.subviews.count ?? "error")
self.navigationController?.view.superview?.insertSubview(self.btnPickup!, at: (self.navigationController?.view.subviews.count)!)
}
override func viewWillDisappear(_ animated: Bool) {
self.btnPickup?.removeFromSuperview()
}
I had exactly this problem. I'm using Interface Builder
and Auto Layout to place it so, it is not a programtic solution like the ones suggest by my colleagues:
The button must be placed at the same level as the UITableView. Take care where you place it in the hierarchy as depicted below: It can not be below the Table View in the hierarchy.
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.