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!)



