Scrollview with multiple images and zoom - ios

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

Related

Horizontal UIScrollView is not centering the current page

I would like to have a horizontal scroll layout which displays images. It works fine if setup 0, 0, 0 and 0 the constraints of the UIScrollView. The problem is exactly when I change the constraints to make margins surrounded the UIScrollView. This is what happens:
First image in the UIScrollView
Second image in the UIScrollView
Third image in the UIScrollView
As you can see, each time you scroll, more off-center the current page is.
I have tried to subtract trailing and leading constrains constants to the width of the scrollLayout, play with frames and bouds but without success.
If I run this example in a smaller display like iphone 5S, the problem is more pointed.
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var pageController: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
let imagesArray = ["b_1", "b_2", "b_3", "b_4", "b_5"]
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.isPagingEnabled = true
self.pageController.numberOfPages = imagesArray.count
self.pageController.pageIndicatorTintColor = UIColor.blue
self.pageController.currentPageIndicatorTintColor = UIColor.gray
for i in 0...imagesArray.count - 1{
let imageView = UIImageView()
imageView.contentMode = .scaleToFill
imageView.image = UIImage(named: self.imagesArray[i])
let xPos = CGFloat(i)*self.view.bounds.size.width
imageView.frame = CGRect(x: xPos, y: 0, width: view.frame.size.width, height: self.scrollView.frame.size.height )
self.scrollView.contentSize.width = view.frame.size.width*CGFloat(i+1)
self.scrollView.addSubview(imageView)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let page = scrollView.contentOffset.x/scrollView.frame.width
self.pageController.currentPage = Int(page)
}
}
So, I would like to know how to always obtain the current image centered.
Thank you
EDITED with Rajesh results and view debug:
I would recommend using a UICollectionView in place of a UIScrollView - otherwise you will be building a lot of the basics from scratch. You can use a collection view that centers the images, make sure paging is enabled and you should get the interface you're looking for. Make sure to adopt / conform to the UICollectionViewDelegate & UICollectionViewDelegateFlowLayout protocols with your view controller & set those delegates on your collection view. Hope that helps! Best of luck.

Swift 3 Horizontal Multiple UIScrollView - Different Speeds

I have created two UIScrollViews ( One named scrollView and one named scrollLevel4 )
when I move scrollLevel4, I can get scrollView to move at the same speed using :-
func scrollViewDidScroll(_ scrollLevel4: UIScrollView) {
scrollView.contentOffset.x = scrollLevel4.contentOffset.x
}
However If I want to move scrollView at a different pace, not sure what to do, whenever I add anything to the end of line :
scrollView.contentOffset.x = scrollLevel4.contentOffset.x
it crashes, even a simple + 10, same pace, staggered offset, still crashes
also tried .scrollRectToVisible() method
Thoughts ?
Without seeing the error or your code it's hard to say for sure, but most likely you are setting the same delegate for both scrollViews. When you drag scrollLevel4, it triggers a scroll on scrollView, so you get an infinite loop and eventually a crash.
If you want to use the same delegate on both scrollViews, you'll need to check which one was passed before operating on them. Here's a basic working implementation. Open a new single view project and replace the code in ViewController.swift with:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
var imageView1: UIImageView!
var imageView2: UIImageView!
var scrollView1: UIScrollView!
var scrollView2: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
imageView1 = UIImageView(image: UIImage(named: "image.jpg"))
imageView2 = UIImageView(image: UIImage(named: "image.jpg"))
scrollView1 = UIScrollView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 200, height: 200)))
scrollView1.contentSize = imageView1.bounds.size
scrollView1.addSubview(imageView1)
view.addSubview(scrollView1)
scrollView2 = UIScrollView(frame: CGRect(origin: CGPoint(x: 0, y: 210), size: CGSize(width: 200, height: 200)))
scrollView2.contentSize = imageView2.bounds.size
scrollView2.addSubview(imageView2)
view.addSubview(scrollView2)
scrollView2.delegate = self
scrollView1.delegate = self
}
func scrollViewDidScroll(_ scrolled: UIScrollView) {
// both scrollViews call this when scrolled, so determine which was scrolled before adjusting
if scrolled === scrollView1 {
scrollView2.contentOffset.x = scrolled.contentOffset.x + 100
} else if scrolled === scrollView2 {
scrollView1.contentOffset.x = scrolled.contentOffset.x - 100
}
}
}
Note that whatever modification you apply to the offset of one, you have to apply the exact inverse (or nothing at all) to the other. Otherwise you'll have an infinite loop of back and forth scrolling ending in a crash.

UITableViewCell Scroll View

I have a table view cell with a scroll view for scrolling through images. There are a couple of issues I'm having with it.
When the table view is shown, the cell with the scroll view and images shows like the first image and part of the second image and the paging doesn't work and it lets you scroll in any direction.
Once I scroll past this cell and then scroll back to it, it is displayed correctly and paging works like it should.
In cellForRowAtIndexPath()
if indexPath.section == 0 {
let imageCell = tableView.dequeueReusableCellWithIdentifier("imageCell", forIndexPath: indexPath) as! ImageCell
imageCell.selectionStyle = .None
if imageFiles.count > 0 {
if imageFiles.count == 1 {
imageCell.pageControl.hidden = true
}
imageCell.pageControl.numberOfPages = imageFiles.count
imageCell.pageControl.currentPage = 0
var index = 0
for photo in imageFiles {
var frame = CGRect()
frame.origin.x = (imageCell.imageScrollView.frame.size.width * CGFloat(index))
frame.origin.y = 0
frame.size = CGSizeMake(imageCell.imageScrollView.frame.size.width, imageCell.imageScrollView.frame.size.height)
let imageView = UIImageView(frame: frame)
imageView.image = photo
imageCell.imageScrollView.addSubview(imageView)
imageCell.imageScrollView.contentSize = CGSizeMake(imageCell.imageScrollView.frame.size.width * CGFloat(imageFiles.count), imageCell.imageScrollView.frame.size.height)
index += 1
}
} else {
imageCell.pageControl.hidden = true
let imageView = UIImageView(frame: imageCell.imageScrollView.frame)
imageView.image = UIImage(named: "placeholder")
imageCell.imageScrollView.addSubview(imageView)
}
My custom cell:
import UIKit
class ImageCell: UITableViewCell, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var imageScrollView: UIScrollView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageWidth = scrollView.frame.size.width
pageControl.currentPage = Int(scrollView.contentOffset.x / pageWidth)
}
}
I was using SDWebImageDownloader. viewDidLoad() to download images to use in the scroll view but I wasn't reloading the table view data. Once I added that, this works perfectly.
func downloadImages() {
let downloader = SDWebImageDownloader.sharedDownloader()
for imageURL in images {
let url = NSURL(string: imageURL)
downloader.downloadImageWithURL(url, options: SDWebImageDownloaderOptions.UseNSURLCache, progress: nil,
completed: { (image, data, error, bool) -> Void in
self.imageFiles.append(image)
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
})
}
}
You are doing this:
frame.size = CGSizeMake(imageCell.imageScrollView.frame.size.width, imageCell.imageScrollView.frame.size.height)
I would guess that when your view initially loads, imageScrollView has not been fully laid out, so it doesn't have a valid size. Scrolling away from this cell & back to it after the view is fully loaded would result in the scroll view having a valid size.
You should validate that imageScrollView has the width you think it does, and if it doesn't, maybe use the width of the screen instead.
I have implemented a similar thing in one of my apps, so as another possible option, instead of putting a scroll view in your first cell, you could place a CollectionView in there instead and populate your collection view with cells of images. This would A) allow your layout to dynamically change with the view, and B) rather than loading up your scroll view with all the images, they can be dynamically loaded as needed.

Animate headerView of TableView while pulling down UITableView in swift

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.

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