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.
Related
I am trying to implement a scrollView that contains a text field, button, and table view. I am completely lost trying to get the thing to scroll, I've tried changing the height of UITableView but that has not worked, I've tried adjusting the height of the content View and scrollView, but that has not worked either. How can I get the scrollview to scroll and adjust the size when another tableViewCell is added to the tableView?
override func viewDidLayoutSubviews() {
tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: CGFloat(tableView.numberOfRows(inSection: 0) * 120))
tableView.reloadData()
}
#IBAction func addExercisePressed(_ sender: Any) {
test.append("nice")
tableView.reloadData()
tableView.frame = CGRect(x: tableView.frame.origin.x,y: tableView.frame.origin.y ,width: self.view.frame.size.width, height: CGFloat(test.count * 120))
scrollView.frame.size = CGSize(width: self.view.frame.size.width, height: tableView.frame.size.height)
}
Please dont use tableView inside a scrollView, tableView is subclass of scrollView and that's mean tableView already have scroll function
your answer to set
tableHeight.constant = tableView.contentSize.height
It will make recycle/dequeue cell function useless because tableView will show all the cell immediately, tableview will call cellForRowAt for all indexes that showed in tableview viewport
UITableview inside Scrollview call cellforrow too many times
Here why we use dequeueReusableCellWithIdentifier
From what I see you want something like this right
you can check this repo how I only use a tableview to achieved that
please note that tableHeaderView is scrollable, sectionHeaderView is sticky
All I needed was to set a height constraint for the TableView and connect that via IBOutlet, then this line of code wherever reloading table data is needed
tableHeight.constant = tableView.contentSize.height
I have created an UIActivityIndicatorView in my UITableViewController in Swift like so:
indicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) as UIActivityIndicatorView
indicator.center = self.view.center
indicator.hidesWhenStopped = true
indicator.style = UIActivityIndicatorView.Style.white
indicator.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
indicator.layer.cornerRadius = 15
self.view.addSubview(indicator)
self.indicator.startAnimating()
And this works like a charm, but when its running and I scroll on my UITableView Controller the UIActivityIndicatorView does not scroll with it. How do I get the UIActivityIndicatorView to scroll with the UITableViewController.
You can add it to the window
let wind = (UIApplication.shared.delegate as! AppDelegate).window!
wind.addSubview(indicator)
To remove
indicator.removeFromSuperview()
You can also make it inside the vc with
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
indicator.frame.origin = CGPoint(x: indicator.frame.origin.x, y: UIScreen.main.bounds.height / 2 - 50 + scrollView.contentOffset.y)
}
Your question and the title seem to ask different things. In the question you ask how to make it move with the scrolling, in the title you ask how not to make it scroll.
There are a few ways to do this (scrolling).
One way is to implement func scrollViewDidScroll(_ scrollView: UIScrollView) { } and move the activity indicator when the scroll view (a UITableView is a scrollView after all) scrolls. An other way (which I personally like more) is to put the activity indicator in a table view cell. Cells always scroll with the table view.
You need to do is set indicator.center to the center of its superview's bounds. Those values are in the same coordinate system (here, cell.like's coordinate system).
indicator.setCenter(CGPointMake(CGRectGetMidX(cell.like.bounds),
CGRectGetMidY(cell.like.bounds)));
I think that the indicator behind UITableView. Do you try to bring indicator to the front?
self.view.bringSubview(toFront: indicator)
Actually it scrolls without any problem, so I think there is no error in your code..
I am trying to create a cover flow image slide something like this :
I have to say I don't want to use others framework like iCarousel I need to write my own code. here is my codes but it only shows me one image per page , I was wondering how can I change my code to add previous and next image like the image ?
override func viewDidLoad() {
super.viewDidLoad()
pageControl.numberOfPages = imageArray.count
scrollView.frame = self.view.frame
scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(pageControl.numberOfPages) , height: 0)
scrollView.delegate = self
for i in 0..<imageArray.count {
ashvanImage = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width - 50 , height: self.view.frame.height - 200))
ashvanImage.center = scrollView.center
ashvanImage.contentMode = .scaleAspectFit
ashvanImage.image = UIImage(named: "\(i).jpg")
scrollView.addSubview(ashvanImage)
createPageWith(images: ashvanImage, page: i)
}
}
func createPageWith(images:UIImageView, page:Int) {
let newView = UIView(frame: CGRect(x: scrollView.frame.size.width * CGFloat(page), y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height))
newView.addSubview(images)
scrollView.addSubview(newView)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let page = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(page)
}
As others have said, if you don't want to use iCarousel, which has CoverFlow built in, you should use a UICollectionView. It is built for what you want.
Certainly you could do this starting from a UIScrollView, but it would probably take an experienced developer several weeks of full-time work to get a clean design working and debugged, and the result would look and feel a whole lot like a collection view when you were done, only not as flexible or as maintainable. Plus you'd need pretty advanced knowledge of Core Animation, which is pretty specialized and not very well documented.
EDIT:
A Google search on "UICollectionView CoverFlow Swift" found this framework on Github:
https://github.com/sumitlni/LNICoverFlowLayout
If you're determined to do this yourself, you could at least look over that framework as a road map for what you'd need to do.
You are set scrollview's width equal to the whole view, make it less like 85% or 90% or according to your requirement.
Which will solve half of your problem.
By looking at the design i think you want the view to animate.
So first, turn off scrolling for the scrollview.
Then add 2 gesture's on the scrollview, one for right swipe and another for left swipe.
Then scroll the view 1 page at a time according to the gesture and animate the view's size by changing its x position by 1 page and decreasing its size and increasing the incoming one's.
You can use the default method UIScrollView.animate too.
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().
My UIScrollView won't scroll down. I don't know why. I already followed Apple documentation regarding to this issue.
#IBOutlet weak var scroller: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
scroller.scrollEnabled = true
// Do any additional setup after loading the view
scroller.contentSize = CGSizeMake(400, 2300)
}
You need to set the frame of your UIScrollView so that it is less than the contentSize. Otherwise, it won't scroll.
Also, I would recommend that you add the following to your viewDidLoad method:
scroller.contentSize = CGSize(width: 400, height: 2300)
If you are using AutoLayout
Set content size in viewDidAppear which works for me.
override func viewDidAppear(_ animated: Bool) {
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height+300)
}
Alot of the time the code is correct if you have followed a tutorial but what many beginners do not know is that the scrollView is NOT going to scroll normally through the simulator. It is suppose to scroll only when you press down on the mousepad and simultaneously scroll. Many Experienced XCode/Swift/Obj-C users are so use to doing this and so they do not know how it could possibly be overlooked by beginners. Ciao :-)
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
// Do any additional setup after the view
}
override func viewWillLayoutSubviews(){
super.viewWillLayoutSubviews()
scrollView.contentSize = CGSize(width: 375, height: 800)
}
This code will work perfectly fine as long as you do what I said up above
Do not give fix height to scroll view and always give top of first subview to scrollview and bottom of last subview to scrollview. By this way scroll view will automatically grow as per the size of contained subviews. No need to give contentSize to the scrollview.It will work for small as well as large size iPhone.
Swift 3.0 version
scroller.contentSize = CGSize(width: scroller.contentSize.width, height: 2000)
If you are using autolayout, then the contentSize property stops working and it will try to infer the content size from the constraints. If that is the case, then your problem could be that you are not defining the necessary constraints to the content view so that the scrollview can infer the content size.
You should define the constraints of your content view to the top and bottom edges of the scrollview.
If you are using Storyboard:
Put your Content view inside the UIScrollView
Add top, bottom, left and right constraints with the scroll view
Add equal heights and widths constraints
For a vertical scroll set the Equal Heights Constraint priority to 250. For a horizontal scroll set the Equal Widths Constraint priority to 250
In my case, I used UIStackView inside UIScrollView.
Added some views-elements from code to stackview.
It won't scroll.
Fixed it by setting stackview's userInteractionEnabled to false.
The problem could be that your scrollView doesn't know its contentSize like stated above, but the fix is easier than what the above answers are. Like Carlos said but I will elaborate more. If you want your scrollView to scroll vertically(up & down), make your contentView which is in the hierarchy of the scrollView equal width to the ViewController and give it a height constraint that works for your project i.e. 700. For the opposite(horizontally) make the height equal to the ViewController and the width some big number that works for your project.
FWIW, I found that I needed to use sathish's solution from above, though it was insufficient to effect the intervention in viewDidAppear alone. I had to instead make the adjustment for every new content assignment:
func display(pattern: Pattern) {
let text : NSAttributedString = pattern.body()
patternTextView.attributedText = text
// Set the size of the view. Autolayout seems to get in the way of
// a correct size calculation
patternTextView.contentSize = CGSize(width: 348, height: 620)
}
The manifest constants (yeah, I know, yuk, but it makes it easier to understand here) are from the autolayout spec.
It worked for me. In Size Inspector
Layout = Translates Mask into constraints.
Autoresizing = all click.
For Swift 5.6 and iOS 15:
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
let subView: UIView = UILabel()
subView.text = String(repeating: "MMMMMMM ", count: 100)
NSLayoutConstraint.activate([
subView.topAnchor.constraint(equalTo: scrollView.topAnchor),
subView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
subView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
subView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
// Constrain width so the label text wraps and we scroll vertically.
subView.widthAnchor.constraint(lessThanOrEqualTo: scrollView.widthAnchor),
])
Increase the content Height work for me.
I do not know it is a good solution, but you can try to set headerview to empty UITableView.
let scrollView: UIView = UIView(frame: CGRectMake(0, 0, 400, 2300))
tableView.tableHeaderView = scrollView