Question or problem with Swift language programming:
Im making an horizontal UICollectionView, and inside UICollectionViewCell i have scrollview and inside the scrollview i have an imageView.
The issue I’m having is that when i zoom-in the imageView,scrollView is taking all the cell size, so its not fitting to the image size height and width.thus by scrolling up and down the image disappear from scrollview, i have no idea whats going wrong in my code.
My ColectionViewCell code:
class CollectionViewCell: UICollectionViewCell { @IBOutlet var scrollView: UIScrollView! @IBOutlet var ImageV: UIImageView! }
CollectionView code :
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell cell.scrollView.contentMode = UIViewContentMode.ScaleAspectFit cell.scrollView.delegate = self cell.ImageV.image = UIImage(named: array[indexPath.row]) cell.ImageV.contentMode = UIViewContentMode.ScaleAspectFit cell.scrollView.minimumZoomScale = 1 cell.scrollView.maximumZoomScale = 4; cell.scrollView.contentSize = cell.ImageV.frame.size return cell } func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { return CGSize(width: self.collectionView.frame.size.width , height: self.collectionView.frame.size.height - 100) } func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { let indexPath = NSIndexPath(forItem: Int(currentIndex), inSection: 0) if let cell1 = self.collectionView.cellForItemAtIndexPath(indexPath) { let cell = cell1 as! CollectionViewCell let boundsSize = cell.scrollView.bounds.size var contentsFrame = cell.ImageV.frame if contentsFrame.size.width < boundsSize.width{ contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2 }else{ contentsFrame.origin.x = 0 } if contentsFrame.size.height < boundsSize.height { contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2 }else{ contentsFrame.origin.y = 0 } return cell.ImageV } return UIView() } func scrollViewDidEndDecelerating(scrollView: UIScrollView) { currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width; oldcell = currentIndex - 1 let indexPath = NSIndexPath(forItem: Int(oldcell), inSection: 0) if let cell1 = self.collectionView.cellForItemAtIndexPath(indexPath) { let cell = cell1 as! CollectionViewCell cell.scrollView.zoomScale = 0 } }
Image preview:
https://i.imgur.com/Gr2p09A.gifv
My project found here : https://drive.google.com/file/d/0B32ROW7V8Fj4RVZfVGliXzJseGM/view?usp=sharing
How to solve the problem:
Solution 1:
Well First lets start with UIViewController
that is holding your UICollectionView
:
Define a variable to hold the collection view layout:
var flowLayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout()
You will have to override viewWillLayoutSubviews
and this is going to handle the collectionView size.
override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() flowLayout.itemSize = CGSize(width: view.frame.width, height:view.frame.height) }
Also you will need to override viewDidLayoutSubviews
to handle the size of each new cell to set it to default size:
override func viewDidLayoutSubviews() { if let currentCell = imageCollectionView.cellForItemAtIndexPath(desiredIndexPath) as? GalleryCell { currentCell.configureForNewImage() } }
in ViewDidLoad
setup the collectionView to be horizontal with flow layout:
// Set up flow layout flowLayout.scrollDirection = UICollectionViewScrollDirection.Horizontal flowLayout.minimumInteritemSpacing = 0 flowLayout.minimumLineSpacing = 0 // Set up collection view imageCollectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height), collectionViewLayout: flowLayout) imageCollectionView.translatesAutoresizingMaskIntoConstraints = false imageCollectionView.registerClass(GalleryCell.self, forCellWithReuseIdentifier: "GalleryCell") imageCollectionView.dataSource = self imageCollectionView.delegate = self imageCollectionView.pagingEnabled = true view.addSubview(imageCollectionView) imageCollectionView.contentSize = CGSize(width: 1000.0, height: 1.0)
In your UICollectionView
method cellForItemAtIndexPath
load the image only without setting anything:
cell.image = UIImage(named: array[indexPath.row])
Now lets move to GalleryCell
class and to handle scrollView when zooming:
class GalleryCell: UICollectionViewCell, UIScrollViewDelegate { var image:UIImage? { didSet { configureForNewImage() } } var scrollView: UIScrollView let imageView: UIImageView override init(frame: CGRect) { imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false scrollView = UIScrollView(frame: frame) scrollView.translatesAutoresizingMaskIntoConstraints = false super.init(frame: frame) contentView.addSubview(scrollView) contentView.addConstraints(scrollViewConstraints) scrollView.addSubview(imageView) scrollView.delegate = self scrollView.maximumZoomScale = 4 } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } internal func configureForNewImage() { imageView.image = image imageView.sizeToFit() setZoomScale() scrollViewDidZoom(scrollView) } public func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { return imageView } public func scrollViewDidZoom(scrollView: UIScrollView) { let imageViewSize = imageView.frame.size let scrollViewSize = scrollView.bounds.size let verticalPadding = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 0 let horizontalPadding = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 0 if verticalPadding >= 0 { // Center the image on screen scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding) } else { // Limit the image panning to the screen bounds scrollView.contentSize = imageViewSize } }
I've tested it with example and it should solve your issue with scrollview and @Spencevail explained mostly why it's being caused !
Solution 2:
You need to set the contentInset
on your scrollview. What's happening is The contentSize
of the UIScrollView
is originally set to match the screen size with the image inside of that. As you zoom in on the scrollview, the contentSize expands proportionately, so those black areas above and below the photos when you're zoomed all the way out expand as you zoom in. In other words You're expanding the area above and below where your image can go. If you adjust the contentInset
it will bring in that dead area and not allow the scrollview to move the image out of the window.
public func scrollViewDidZoom(scrollView: UIScrollView) { let imageViewSize = imageView.frame.size let scrollViewSize = scrollView.bounds.size let verticalPadding = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 0 let horizontalPadding = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 0 if verticalPadding >= 0 { // Center the image on screen scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding) } else { // Limit the image panning to the screen bounds scrollView.contentSize = imageViewSize } }
This looks almost the same as an open source Cocoapod I help manage, SwiftPhotoGallery. Take a look at the code for the SwiftPhotoGalleryCell
and you should get a pretty good idea of how to do this. (Feel free to just use the cocoapod too if you want!)