Swift: ScrollView Scrolling as per Cell Image Size - ios

I have a viewController which has CollectionView and the collectionView cell contain the ImageView and response is from the api. And the viewController orientation is Landscape. So issue is when i scroll the image it's not scrolling properly. Here is the attach image.
So how can I set the Scrolling action as per the my image Size. So it's display only image in View. Here is my code.
extension GalleryViewController : UICollectionViewDataSource , UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! ImageCell
let image = arrImgs[indexPath.row]
cell.lblIndex.text = "< \(indexPath.row+1) / \(arrImgs.count) > Images"
if let imgUrl = URL.init(string: image.Message) {
cell.imgV.sd_setImage(with: imgUrl) { (img, error, casheType, url) in
DispatchQueue.main.async {
cell.imgV.image = img
}
}
}
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrImgs.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: width, height: height)
}
}
class ImageCell: UICollectionViewCell {
#IBOutlet var lblIndex: UILabel!
#IBOutlet var imgV: UIImageView!
}
This is my StoryBord Arrangement.
Thanks.

Related

how to filtering collectionvew cell?

I have a collectionview cell, and need to filtering cell.
I'm trying to make cell height to 0. but not work.
this is my code.
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.numberOfCell
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? data else {
return UICollectionViewCell()
}
let set1 = CoreDataManager.shared.getSetting(idx: indexPath.row).set1
if(set1){
return cell
}
else{
let none = collectionView.dequeueReusableCell(withReuseIdentifier: "none", for: indexPath)
none.frame.size.width = 100
none.frame.size.height = CGSize(width: 0, height: 100).width
return none
}
}
}
If you need to set a size of cell you need to conform to UICollectionViewDelegateFlowLayout and implement sizeForItemAt and give it the size.
extension viewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 0)}
}
another solution to make height constrain for a view inside the your cell with identifier "cell" and give it to 0 before returning a cell in cellForItemAt.

Data not displaying correctly in CollectionView cell?

I have trying to make a program that will display the image of crystals. After writing the code and formatting the StoryBoard, when I run the app the images are tiny and there are no labels displayed. I have constraints.
Here is the code:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
let imageArray = [UIImage(named: "1"),UIImage(named: "2"),UIImage(named: "3")]
let nameArray = ["Rose Quartz", "Clear Quartz", "Clear Quartz 2"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainCollectionViewCell", for: indexPath) as! MainCollectionViewCell
cell.crystalPhotoImageView.image = imageArray[indexPath.row]
cell.crystalNameLabel.text! = nameArray[indexPath.row]
return cell
}
}
you need to implement to confirm the class has UICollectionViewDelegateFlowLayout and you need to implement this functionn
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return cellSize
}

Call collectionView(_ :layout:sizeForItemAt:) in Swift 4

I made the gallery using UICollectionView.
If you lower the screen, the image should continue to come out and stop at 20.
The code was constantly updated.
However, the function collectionView (_: layout: sizeForItemAt :) that sets the size of the cell did not work.
Therefore, only 20 images are displayed continuously.
On viewDidLoad()
if let layoutt = artCollectionView?.collectionViewLayout as? PinterestLayout
{
layoutt.delegate = self
}
cell setting
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionViewCell", for: indexPath) as? HomeCollectionViewCell,
let arts = self.artList else { return HomeCollectionViewCell() }
if arts.count > indexPath.row
{
let model = arts[indexPath.row]
cell.imgView.sd_setImage(with: URLHelper.createEncodedURL(url: model.thumburl), completed: nil)
}
return cell
}
return cell size
extension HomeViewController : PinterestLayoutDelegate
{
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat
{
return CGFloat(300.0)
}
}
Currently, the function only executes once when the app starts. Do you know how to run that function every time I want?
You need to implement UICollectionViewDelegateFlowLayout.
You can read more about the protocol here:
UICollectionViewDelegateFlowLayout protocol
Example:
extension viewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 50, height: 300)
}
}
If you look at the prepare() method of PinterestLayout class, you can see the below code
guard
cache.isEmpty == true,
let collectionView = collectionView
else {
return
}
When you reload the colectionView the cache is no longer empty, so it returns back without altering the layout.
So just remove the cache value when prepare is called.
Add removeAll() before guard
cache.removeAll()
Please follow the steps to install PinterestLayout in UICollectionView below.
Set PinterestLayout to your collection view.
let layout = PinterestLayout()
collectionView.collectionViewLayout = layout
Setup layout
layout.delegate = self
layout.cellPadding = 5
layout.numberOfColumns = 2
Implement methods of PinterestLayoutDelegate
extension CustomCollectionVC: PinterestLayoutDelegate {
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
let image = images[indexPath.item]
return image.height(forWidth: withWidth)
}
func collectionView(collectionView: UICollectionView,
heightForAnnotationAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
let textFont = UIFont(name: "Arial-ItalicMT", size: 11)!
return "Some text".heightForWidth(width: withWidth, font: textFont)
}
}
When data is loaded invalidate layout as well as reload data on collection view.
collectionView.collectionViewLayout.invalidateLayout()
collectionView.reloadData()
Observation:
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat
try this instead of above:
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat

UICollectionView cellForItem(at:) crashes

I am using Swift4 and Xcode9.
I create my collectionView cells by using the delegate method below;
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
return cell
}
Then, I want to replace the image of selected cell with a "stop" image. To achieve this, I have tried to use didSelectItemAt method;
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (mediaPlayer?.isPlaying == true) {
// stop the player
mediaPlayer?.stopPlayingMedia()
} else {
// change the image and start playing
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell // CRASH
cell.mediaItemCellImageView.image = UIImage(named: "stop.png")
...
...
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
// stop playing
mediaPlayer?.stopPlayingMedia()
}
Now, when I select the cell which is already selected (hence the original image has already been replaced with "stop.png") the code above works; i.e. the original image is restored, which is done by stopPlayingMedia() method.
When I select a cell different than the currently selected one, the App crashes. I tried to move the didSelectItemAt call out of the delegate method, but it did not work.
Surprisingly, the logical check if collectionView.cellForItem(at: indexPath) is MediaItemCollectionViewCell succeeds when I select the currently selected cell, and fails when I select another cell.
I want to change the image of the cell, hence need to cast the variable type but it fails. Why does to cast fail when I select a different cell than the currently selected one?
Thanks
Edit:
I use collectionView.reloadData() to restore all the images to originals (I mentioned this in the OP - when stopPlayingMedia() is invoked). It appears that, if I add a private var index:IndexItem? to my ViewController class and update its value within didSelectItemAt (to save the last selected cell) and use the code below, the code does not crash
extension ViewController:MediaPlayerControllerDelegate {
// this delegate method is invoked when stopPlayingMedia() is called
func mediaPlayerControllerDidFinishPlaying() {
// restore the UI
// collectionView.reloadData() -> this causes the crash, instead, I only restore one cell
let cell = collectionView.cellForItem(at: index!) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: (index?.row)!)?.image
timeLabel.text = "00:00"
progressBar.progress = 0.0
}
}
I tried the following to create cell and changing the image inside cell on selection. Not crashing at all. Please check the implementation, It might help you.
import UIKit
class ViewController: UIViewController {
private var isPlayingModel = [false, false, false, false, false] // initially all are stop, not playing
#IBOutlet weak var collectionView: UICollectionView! {
didSet{
collectionView.delegate = self
collectionView.dataSource = self
}
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isPlayingModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
isPlayingModel[indexPath.row] = !isPlayingModel[indexPath.row]
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.size.width, height: 60)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
}
class MediaItemCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageVw: UIImageView!
}
Check the implementation here.
You are not passing indexPath correctly to load a cell, that's why it is crashing.Edit your didSelectItemAt: method as follows:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCell = IndexPath(item: indexPath.item, section: indexPath.section)
let cell = collectionView.cellForItem(at: selectedCell) as! ACollectionViewCell
cell.imgView.image = UIImage(named: "3.jpg")
}
Collection view indexPath has element item, and not row
Try this and see: (let me know what does it print)
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
if let cell = collectionView.cellForItem(at: indexPath) as? MediaItemCollectionViewCell {
if let cellImage = mediaPlayer?.item(at: indexPath.item)?.image { // Collection view indexPath has element `item`, and not `row`
cell.mediaItemCellImageView.image = cellImage
} else {
print("cellImage not found at indexPath - \(indexPath)")
}
} else {
print("collection view cell `MediaItemCollectionViewCell` not found at indexPath -\(indexPath) ")
}
// stop playing
mediaPlayer?.stopPlayingMedia()
}

UICollectionView with array of images set it right

I have a question. Inside UICollectionView I have UIImage with Content Mode - Aspect Fit... Now I need that in UICollectionView images show one near another. Now I have a lot of space between them.
extension UserDetailInformationOfAnimalViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
//MARK: - Collections
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let post = postsArray[collectionView.tag]
// print("count: ", post.imagesLink.count)
return post.imageLinks.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
let post = postsArray[collectionView.tag]
// print("postArrayPhotos: ", post.imagesLink.count)
if post.imageLinks.count != 0 {
// print("POST = ", post)
// print("Look here: ", post.imagesLink[indexPath.row].imageLink)
// print("IMAGELINK = ", post.imagesLink[indexPath.row].imageLink)
let imageLink = post.imageLinks[indexPath.row]
if imageLink.imageLink != nil {
let url = URL(string: imageLink.imageLink!)
print("IMAGELINK = ", imageLink)
cell.imageOfAnimalInCollectionView.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
})
}
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = self.view.frame.size.height;
let width = self.view.frame.size.width;
// in case you you want the cell to be 40% of your controllers view
return CGSize(width: width, height: height * 0.35)
}
}
Use this method to set width of cell dynamically
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if indexPath.row == whatever you want {
return CGSize(width: collectionView.frame.size.width/2, height: collectionView.frame.size.width/2)
}
}
I understand your problem. Here I give you a way around. This solution may not effective for you but it will give you idea how to solve it. Main concept is the you have to return cell size according to image. But instantly image is not in your hand. Its on the server. So you can do that fist return a demo or place holder image height then after finishing download actual image from server tell the collection view to re-layout the cell according to actual image size.
cell.imageOfAnimalInCollectionView.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
//told the collection view to relayout according to image size
collectionView.collectionViewLayout.invalidateLayout()
})
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.cellForItem(at: indexPath) as! CollectionViewCell
return cell.imageOfAnimalInCollectionView.image.size! // Put your logic here, until actual image download from server you can return a default size.
}

Resources