Animate headerView of TableView while pulling down UITableView in swift - ios

I am trying to create a growing UITableViewHeader on UITableView. I have a UITableView and a mapView set in the tableHeaderView of UITableView.
tblView.bounces = true
tblView.bouncesZoom = true
tblView.alwaysBounceVertical = true
mapView.frame = CGRectMake(0, 0, self.view.frame.size.width, CGFloat(kMapHeaderHeight))
mapView.mapType = MKMapType.Standard
mapView.zoomEnabled=true
mapView.scrollEnabled = true
mapView.delegate = mapHelper
tblView.tableHeaderView = mapView
And also implemented scrollViewDidScroll delegate and whenever it scrolls down, I have changed the frame of headerview as
func scrollViewDidScroll(scrollView: UIScrollView) {
var scrollOffset = scrollView.contentOffset.y
println("\(scrollOffset)")
var headerFrame : CGRect = self.mapView.frame
if (scrollOffset < 0){
headerFrame.size.height -= scrollView.contentOffset.y/3
}
self.mapView.frame = headerFrame
}
However, it does not grow as expected without bouncing.Seems very unclear. Any help?
I am following these tutorials to create a Growing UITableViewheader when pulling down as
UITableVIew header without bouncing when pull down ,
Expand UITableView Header View to Bounce Area When Pulling Down
Here is the link of the project :
https://drive.google.com/file/d/0B6dTvD1JbkgBVENUS1ROMzI0Wnc/vie
EDITED: i somehow managed to have the effect but the animation seems very slow
func scrollViewDidScroll(scrollView: UIScrollView) {
let yPos: CGFloat = -scrollView.contentOffset.y
if (yPos > 0) {
var mapViewRect: CGRect = self.mapView.frame
mapViewRect.origin.y = scrollView.contentOffset.y
mapViewRect.size.height = kHeaderHeight+yPos
self.mapView.frame = mapViewRect
}
}
let kHeaderHeight:CGFloat = 200

I suggest you to use this tutorial.
The most important parts of it:
Create a scrollView (or whatever that is a subclass of a UIScrollView) and a separate view (which will be functioning as a headerView, so let's call if headerView)
add the headerView and the scrollView as a subView of your view
implement the scrollViewDidScroll method, and put the framing logic there (of course, if you're using autolayout, you have to manage constraints there)

Actually the animation was not working well in simulator of xcode6.3. I tried 2 days for this and posted a bounty here but when i finally I tested it on real device and found the MapView was properly bouncing.If anyone needs it..here is the piece of logic.
let kHeaderHeight:CGFloat = 380
class NewBookingVC: UIViewController {
#IBOutlet weak var tblView: UITableView!
let mapView : MKMapView = MKMapView()
var customTableHeaderView:UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
tblView.delegate = self
tblView.dataSource = self
mapView.frame = CGRectMake(0, 0, self.view.frame.size.width, 380)
mapView.mapType = MKMapType.Standard
mapView.zoomEnabled=true
mapView.scrollEnabled = true
customTableHeaderView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, 380))
customTableHeaderView.addSubview(mapView)
tblView.tableHeaderView = customTableHeaderView
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let yPos: CGFloat = -scrollView.contentOffset.y
if (yPos > 0) {
var mapViewRect: CGRect = self.mapView.frame
mapViewRect.origin.y = scrollView.contentOffset.y
mapViewRect.size.height = kHeaderHeight+yPos
self.mapView.frame = mapViewRect
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Actually what you have to do is implement the scrollview delegate for table view because it inherits from scrollview and you can implement scrollViewDidScroll delegate and whenever it scrolls down, change the frame of headerview.
Idea behind this while scrolling you always have to keep the y-position of the MKMapView at the zero position....and height should incerase accordingly...
func scrollViewDidScroll(scrollView: UIScrollView) {
let yPos: CGFloat = -scrollView.contentOffset.y
if (yPos > 0) {
//adjust your mapview y-pos and height
//adjusting new position is all about real math
}
}
Performance in the iOS Simulator is not expected to match performance on device. The iOS Simulator is meant as a tool for rapid prototyping and fast iteration.In your case too, redrawing of MapView performance is quite slow in the simulator because at each Scroll you are calculating its new frame and height. So it takes some time adjusting new frame and seems very slow.
However it works well while tuning on real devices.

Related

Scrollview with multiple images and zoom

I have a problem zooming images in my scrollview.
When there is only one image then it is working fine but when there are more images then the following images are on top of the first.
They keep there position but first is larger.
Do I have to update the offset of the following images or is there another approach?
When I zoom an
#IBOutlet weak var mainScrollView: UIScrollView!
var imageView: UIImageView!
override func viewDidLoad() {
...
self.mainScrollView.delegate = self
self.mainScrollView.minimumZoomScale = 1.0
self.mainScrollView.maximumZoomScale = 3.0
self.mainScrollView.isPagingEnabled = true
self.mainScrollView.isScrollEnabled = true
self.loadImages()
...
}
#objc fund loadData() {
...
scrollViewWidth = self.mainScrollView.frame.width
scrollViewHeight = self.mainScrollView.frame.height
for dImage in self.imgData! {
let imageView = UIImageView(frame: CGRect(x:offset, y:0,width:self.scrollViewWidth, height:self.scrollViewHeight))
imageView.image = UIImage(data: dImage)
imageView.contentMode = .scaleAspectFit
self.imgViewArray.append(imageView)
self.mainScrollView.addSubview(imageView)
offset += self.scrollViewWidth
self.mainScrollView.contentSize = CGSize(width: offset, height: self.scrollViewHeight)
}
}
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
let page = scrollView.contentOffset.x /
scrollView.frame.size.width;
self.imageView = imgViewArray[Int(page)]
return self.imageView
}
Here I zoomed the another image. Also wrong position.
Would be nice if someone can point me into the right direction.
Thank You!
Add auto layout constraints to position the image views (spacing one below the other) within your scroll view's content view.
UIScrollView tutorial to scroll and zoom content

Detect when y offset changes in scrollView with paging enabled

I would like to find out which page is visible to the user and also do a certain action when a change in the page is detected. To do this I am using the function scrollViewDidScroll. As I didn't get the result I wanted (nothing happened), I looked for answers on stackoverflow and found the following question which was answered: Detecting UIScrollView page change, to be more precise I used the answer by Michael Waterfall and converted the Objective-C code to swift code.
Below you will find the code I used to create the scrollView in my class HomeController: UIViewController, UIScrollViewDelegate {. Moreover the function by Michael Waterfall is declared and called in the function initiateScrollView(). However, the function doesn't print anything. Do you know where I am doing something wrong? I can't see where I made a mistake. Nothing happens when I scroll (e.g. from page 1 to page 2 (homeView to view2)). The function only prints before: 0 once and after that nothing happens.
Code
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
initiateScrollView()
}
func initiateScrollView() {
//create scrollView with paging enabled
let scrollView = UIScrollView(frame: view.bounds)
scrollView.isPagingEnabled = true
view.addSubview(scrollView)
//get page size
let pageSize = view.bounds.size
//individual views
let homeView = UIView()
homeView.backgroundColor = .green
let view2 = UIView()
view2.backgroundColor = .blue
let view3 = UIView()
view3.backgroundColor = .red
//array with individual views
let pagesViews = [homeView, view2, view3]
//amount of views
let numberOfPages = pagesViews.count
print(numberOfPages) //prints '3'
//add subviews (pages)
for (pageIndex, page) in pagesViews.enumerated(){
//add individual pages to scrollView
page.frame = CGRect(origin: CGPoint(x:0 , y: CGFloat(pageIndex) * pageSize.height), size: pageSize)
scrollView.addSubview(page)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
var previousPage = 0
let fractionalPage: Float = Float(scrollView.contentOffset.y / pageSize.height)
let page = lround(Double(fractionalPage))
print("before:", page) //prints 'before: 0' once
if previousPage != page {
// Page has changed, do your thing!
// ...
// Finally, update previous page
print("before:", page)
previousPage = page
print("after:", page)
} //never prints anything
}
scrollViewDidScroll(scrollView: scrollView)
//define size of scrollView
scrollView.contentSize = CGSize(width: pageSize.width, height: pageSize.height * CGFloat(numberOfPages))
}
You forgot to set the viewController as the delegate for the scroll view, as following:
func initiateScrollView() {
//create scrollView with paging enabled
let scrollView = UIScrollView(frame: view.bounds)
scrollView.isPagingEnabled = true
scrollView.delegate = self
view.addSubview(scrollView)
(...) }
And I recommend that you use the scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) function, instead of scrollViewDidScroll(...)
This is because you haven't set the delegate property of your scrollview.
Add scrollView.delegate = self in your initiateScrollView method.

Nested UICollectionView does not hide NavigationBar on swipe

I have a UICollectionViewController (embedded in a NavigationViewController), which scrolls a UICollectionView horizontally via paging through some sections:
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
}
collectionView?.backgroundColor = .white
collectionView?.register(FeedCell.self, forCellWithReuseIdentifier: cellId)
//collectionView?.contentInset = UIEdgeInsetsMake(MenuBar.height, 0, 0, 0)
//collectionView?.scrollIndicatorInsets = UIEdgeInsetsMake(MenuBar.height, 0, 0, 0)
collectionView?.isPagingEnabled = true
Each section or page contains another UICollectionView (inside the FeedCell) which scrolls vertically through some UICollectionViewCells.
Inside the UICollectionViewController, I set
navigationController?.hidesBarsOnSwipe = true
which was working as long as there was only one UICollectionView. But since the (Top)CollectionView is scrolling horizontally and is containing additional (Sub)CollectionView, that are scrolling vertically, this feature seems not to work any longer.
I would like the NavigationBar to hide when the (Sub)CollectionView is scrolling vertically. Is there any hack to achieve this?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let navigationBar = self.navigationController?.navigationBar {
let clampedYOffset = contentOffset.y <= 0 ? 0 : -contentOffset.y
navigationBar.transform = CGAffineTransform(translationX: 0, y: clampedYOffset)
self.additionalSafeAreaInsets.top = clampedYOffset
}
}
This is a solution that I came up with. Basically modify the transform of the NavigationBar to move it out the way when necessary. I also modify the additionalSafeAreaInset, as this will automatically shift all your content up to fill the space left by the navigation bar.
This function will be called as part of the UICollectionViewDelegate protocol.
This was suitable for my purposes - but if you want the navigation bar to appear when the user rapidly scrolls up (like in safari) you will have to add some additional logic.
Hope this helps!
You can try the code like this (Swift 3.0):
extension ViewController: UICollectionViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let isScrollingUp = scrollView.contentOffset.y - lastY > 0
lastY = scrollView.contentOffset.y
self.navigationController?.setNavigationBarHidden(isScrollingUp, animated: true)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
// show navigation bar ?
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
// show navigationBar ?
}
}

Swift: Updating UIPageControl from ScrollView

I seem to be having some issues getting the UIPageControl to work.
I have a ViewController that holds a ScrollView. This ScrollView loads nib files that can be swiped. See image:
Here is the code that loads these:
self.addChildViewController(vc0)
self.displayReportView.addSubview(vc0.view)
vc0.didMoveToParentViewController(self)
var frame1 = vc1.view.frame
frame1.origin.x = self.view.frame.size.width
vc1.view.frame = frame1
self.addChildViewController(vc1)
self.displayReportView.addSubview(vc1.view)
vc1.didMoveToParentViewController(self)
// And so on
...
This works fine as in they scroll correctly etc..
Now, on the ViewController (one holding the scrollview) I added the delegate:
UIScrollViewDelegate
created some variables:
var frame: CGRect = CGRectMake(0, 0, 0, 0)
var colors:[UIColor] = [UIColor.redColor(), UIColor.blueColor(), UIColor.greenColor(), UIColor.yellowColor()]
var pageControl : UIPageControl = UIPageControl(frame: CGRectMake(50, 300, 200, 20))
I added some functions that are needed:
func configurePageControl() {
// The total number of pages that are available is based on how many available colors we have.
self.pageControl.numberOfPages = 4
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.redColor()
self.pageControl.pageIndicatorTintColor = UIColor.blackColor()
self.pageControl.currentPageIndicatorTintColor = UIColor.greenColor()
self.view.addSubview(pageControl)
}
// MARK : TO CHANGE WHILE CLICKING ON PAGE CONTROL
func changePage(sender: AnyObject) -> () {
let x = CGFloat(pageControl.currentPage) * displayReportView.frame.size.width
displayReportView.setContentOffset(CGPointMake(x, 0), animated: true)
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
Now, When I run the app the scrollview dots show, but when I swipe they do not update.
Question
How do I update the dots to reflect what view is showing?
let me know if you need anything else from my code to see functionality.
You can certainly do what you're describing, if you have a paging scroll view; I have an example of it that uses this code:
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let x = scrollView.contentOffset.x
let w = scrollView.bounds.size.width
pageControl.currentPage = Int(x/w)
}
Except for your round, that looks a lot your code, which makes me think that your code should work. That makes me think that something else is just misconfigured. Is this a paging scroll view? Did you remember to make this object your scroll view's delegate? Use logging or a breakpoint to be certain that your scrollViewDidEndDecelerating is even being called in the first place.
However, I would just like to point out that the configuration you are describing is effectively what UIPageViewController gives you for free — a scroll view with view controller views, plus a page control — so you might want to use that instead.
I would replace the scroll view with a UICollectionView. This way you get paging for free, and it will be better, because paging will work out of the box, without you having to calculate the frame offsets.
Be sure to set collectionView.pagingEnabled = true
To get the current page number, do collectionView.indexPathsForVisibleItems().first?.item
To change the page:
collectionView.scrollToItemAtIndexPath(newIndexPath, atScrollPosition: CenteredHorizontally, animated: true)

UIScrollView and Page Control, Page Control Disrupting View

So, I know its been done many times before, but I am building a ImageViewer with a UIScrollView and a UIPageControl. I am building it in Swift. Yet I have come across a issue which I can't seem to diagnose. I have a interesting setup to minimize long pages of code by splitting a view into containers. One for a tableview, the other for paging images. Here is a image of the storyboard. http://imgur.com/ZqvNgjy So in the first container view, it is a simple UIViewController with a UIScrollView and a UIPageControl. I can confirm that the UIPageControl is not in the view hierarchy of the UIScrollView. Now here is the code I used to setup everything.
import UIKit
//Set Image Array
var imagelist: String[] = ["Home Image 1.png", "info.png"]
//Set Page Numbers
var numpages = imagelist.count
class HomeImageView: UIViewController, UIScrollViewDelegate {
//IBOutlets & Propertys
#IBOutlet var scrollView: UIScrollView
#IBOutlet var pageControl: UIPageControl
override func viewDidLoad() {
super.viewDidLoad()
//Setup Page Control
pageControl.numberOfPages = numpages
pageControl.currentPage = 0
//Setup Scroll View
scrollView.pagingEnabled = true;
scrollView.scrollEnabled = true
scrollView.showsHorizontalScrollIndicator = false;
scrollView.showsVerticalScrollIndicator = false;
scrollView.bounces = true;
scrollView.scrollsToTop = false
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * CGFloat(numpages), scrollView.frame.size.height)
}
override func viewWillAppear(animated: Bool) {
//Setup UIViews with Images
for var z = 0; z < numpages; z++ {
let mainFrame: CGRect = CGRectMake(scrollView.frame.size.width * CGFloat(z), 0, scrollView.frame.size.width, scrollView.frame.size.height)
var image: UIImageView = UIImageView(frame: mainFrame)
image.image = UIImage(named: imagelist[z])
image.clipsToBounds = true
image.contentMode = UIViewContentMode.ScaleAspectFit
scrollView.addSubview(image)
}
func scrollViewDidScroll(scrollview: UIScrollView) {
var pageSize: Float = Float(scrollView.bounds.size.width)
var page: Float = floorf((Float(scrollview.contentOffset.x) - pageSize / 2.0) / pageSize) + 1
// Bound page limits
if (page >= Float(numpages)) {
page = Float(numpages) - 1;
} else if (page < 0) {
page = 0;
}
pageControl.currentPage = Int(page)
println(pageControl.currentPage)
}
}
}
So with that out of the way, here is the problem. When I run it, it does not do exactly what I expected. The simulator starts like this http://imgur.com/RVlKxdf and I can move the view around outside the bounds of the image.. Yet when I change the view hierarchy so that UIPageControl is behind the UIScrollView (Basically Removing it). The problem disappears completely. Aligned and allowing movement only in the bounds like I want. I don't know why the UIScrollView would react to the UIPageControl when it is outside its view hierarchy. Thanks for any assistance.

Resources