Swift How to move indexpath.item next index cell - swift

I want to put all my photo albums in index 0 in the UICollectionViewCell and after another user album (index from 1) in the index cell, but I don't know how to do that. Below is the code I wrote. Please help me.
extension CollectionViewController : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
userCollections = PHAssetCollection.fetchAssetCollections( with: .smartAlbum, subtype: .albumRegular, options: nil)
let numOfItem = userCollections.count
return numOfItem + 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell else { return UICollectionViewCell()}
var coverAsset: PHAsset?
let collection = userCollections[indexPath.row]
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
if indexPath.item == 0 {
fetchReuslt = PHAsset.fetchAssets(with: fetchOptions)
coverAsset = fetchReuslt.firstObject
if let asset = coverAsset {
imgManger.requestImage(for: asset , targetSize: CGSize(width: cell.bounds.width , height: cell.bounds.height - 100), contentMode: .aspectFill, options: nil, resultHandler: { img, _ in
cell.collectionListCoverImg.image = img
})
}
cell.update(title: "All Photos", count: fetchReuslt.count)
} else {
let fetchedAssets = PHAsset.fetchAssets(in: collection, options: fetchOptions)
coverAsset = fetchedAssets.firstObject
if let asset = coverAsset {
imgManger.requestImage(for: asset , targetSize: CGSize(width: cell.bounds.width , height: cell.bounds.height - 50), contentMode: .aspectFill, options: nil, resultHandler: { img, _ in
cell.collectionListCoverImg.image = img
})
}
cell.update(title: collection.localizedTitle, count: fetchedAssets.count)
}
return cell
}

Related

Why is the image quality so low? (swift)

I didn't like apples image picker so I decided to implement my own. I just finished the stage of getting all the users photos and displaying them in a collection view although I noticed that the difference in image quality is horrible. Here is my code:
import UIKit
import Photos
import PhotosUI
import Foundation
private let reuseIdentifier = "Cell"
var selectedImage = UIImage()
class CollectionVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var imageArray = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
grapPhotos()
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath)
let imageView = cell.viewWithTag(1) as! UIImageView
cell.layer.cornerRadius = 4
imageView.image = imageArray[indexPath.row]
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedImageName = imageArray[indexPath.item]
print(selectedImageName)
selectedImage = selectedImageName
performSegue(withIdentifier: "Custom", sender: self)
}
func grapPhotos() {
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchOptions.predicate = NSPredicate(format: "mediaType = %d || mediaType = %d", PHAssetMediaType.image.rawValue, PHAssetMediaType.video.rawValue)
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count {
imgManager.requestImage(for: fetchResult.object(at: i), targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: requestOptions, resultHandler: {
image, error in
self.imageArray.append(image!)
})
}
}
else {
self.collectionView?.reloadData()
print("No Photos")
}
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width / 3 - 6
return CGSize(width: width, height: width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 6.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 6.0
}
}
I don't really know much about working with images so if anyone could help me out on displaying higher quality images that would be great.
This really works))
for index in 0..<fetchResult.count {
let asset = fetchResult.object(at: index) as PHAsset
let sizeFactor = UIScreen.main.scale
let deviceSize = UIScreen.main.nativeBounds.size
manager.requestImage(for: asset,
targetSize: CGSize(width: deviceSize.width * sizeFactor,
height: deviceSize.height * sizeFactor),
contentMode: .aspectFit,
options: requestOptions,
resultHandler: { (uiimage, info) in
if let image = uiimage {
allImages.append(image)
}
})
}
You only need to know ->
let sizeFactor = UIScreen.main.scale
let deviceSize = UIScreen.main.nativeBounds.size
Image quality depends on the phone your viewing on - retina screens require more pixels. You'll need to multiply your targetSize by UIScreen.main.scale.
Try setting targetSize: CGSize(width: 200 * UIScreen.main.scale, height: 200.0 * UIScreen.main.scale) in your imgManager.requestImage function.
For Retina displays, the scale factor may be 3.0 or 2.0 and one point can represented by nine or four pixels, respectively. For standard-resolution displays, the scale factor is 1.0 and one point equals one pixel.

Passing images between controllers in full resolution

My problem is that that when I'm passing image from one controller to another somehow the image looks very bad (very low resolution). The problem is not in passing images but that I'm requesting image in a wrong way.
public var pickedImage: UIImage!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let asset = images[indexPath.row]
let manager = PHImageManager.default()
manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize,
contentMode: .aspectFit,
options: nil) { (result, _) in
self.pickedImage = result
}
self.performSegue(withIdentifier: "showDetailSegue", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetailSegue" {
let dvc = segue.destination as! ViewController
dvc.vcImage = pickedImage
}
}
Fun fact. When I'm displaying images in collectionView (in the same viewcontroller from which I'm trying to send) they're in full resolution even when I'm using same code as mentioned above.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photocell", for: indexPath) as! PhotoLibraryCollectionViewCell
cell.image.layer.cornerRadius = 10
cell.image.clipsToBounds = true
let asset = images[indexPath.row]
let manager = PHImageManager.default()
if cell.tag != 0 {
manager.cancelImageRequest(PHImageRequestID(cell.tag))
}
cell.tag = Int(manager.requestImage(for: asset,
targetSize: PHImageManagerMaximumSize,
contentMode: .aspectFill,
options: nil) { (result, _) in
cell.image.image = result
})
return cell
}
Example: Original
With requestImage
Found solution to my problem.
func convertImageFromAsset(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var image = UIImage()
option.isSynchronous = true
manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
image = result!
})
return image
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
pickedImage = convertImageFromAsset(asset: images[indexPath.item])
self.performSegue(withIdentifier: "showDetailSegue", sender: nil)
}

swift UICollectionView similar preview and drag items

I'm a new in swift and I'm developing UICollectionView drag and drop.
Now I have almost done but I stuck into a few issues.
I want to remove transparent Preview and remove cross button.
And make preview the same as draggable object.
What I want to get:
Here is my code:
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let photo = presenter.petPhotos[indexPath.row]
guard let cell = (collectionView.cellForItem(at: IndexPath(row: 2, section: 0)) as? PetPhotoCollectionViewCell) else {return []}
let cellSize = cell.mainImage.frame.size
guard let thumbnail = photo.path else {
return []
}
let itemProvider = NSItemProvider(object: photo.path! as NSString)
let dragItem = UIDragItem(itemProvider: itemProvider)
dragItem.localObject = photo
dragItem.previewProvider =
{ () -> UIDragPreview in
let image = UIImage(contentsOfFile: (URL(string: thumbnail)?.path)!)
let imageView = UIImageView(image: image)
imageView.bounds.size = cellSize
imageView.frame = imageView.contentClippingRect
imageView.cornerRadius = 9
imageView.layer.shadowColor = UIColor.black.cgColor
imageView.layer.shadowOffset = CGSize(width: 0, height: 2.0)
imageView.layer.shadowRadius = 3.0
imageView.layer.shadowOpacity = 0.5
imageView.layer.masksToBounds = true
imageView.layer.shadowPath = UIBezierPath(roundedRect: imageView.bounds, cornerRadius: imageView.layer.cornerRadius).cgPath
return UIDragPreview(view: imageView)
}
return [dragItem]
}
public func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
let cell = collectionView.cellForItem(at: IndexPath(row: 2, section: 0)) as? PetPhotoCollectionViewCell
let previewParameters = UIDragPreviewParameters()
previewParameters.visiblePath = UIBezierPath(roundedRect: (cell?.mainImage.frame)!, cornerRadius: 9)
return previewParameters
}
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}

UICollectionView didSelectItemAt() and adding/removing a view based on state

I would like to add a checkmark to a selected cell. If the cell is then deselected (and thus another is selected), I would like to remove the checkmark. The top image view is independent from the collection view and represents the selected cell from a user's Camera Roll. The collection view's data source is the list of images fetched from PHAssets.
The problem is that if I don't reload the item at the indexPath, the deselected cell checkmark doesn't get removed. Also, if I scroll through the collection view, I'll see random checkmarks from cells I didn't even tap. I'm assuming it has to do with the reusing of the cells. Can anyone please help me?
Below is snippets of my code from the view controller and the cell...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoCellReuseIdentifier, for: indexPath) as? PhotosCell else { fatalError("Couldn't return a photo cell") }
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.isNetworkAccessAllowed = true
requestOptions.deliveryMode = .highQualityFormat
photoManager.requestImage(for: photoAssets![indexPath.row], targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: { (image, nil) in
cell.photoView.image = image
})
if cell.isSelected {
cell.setupCheckmarkBanner()
}
else {
cell.removeCheckmarkBanner()
}
if indexPath.row == 0 && self.isFirstLoad {
self.selectedImageView.image = cell.photoView.image
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: UICollectionViewScrollPosition.bottom)
cell.setupCheckmarkBanner()
self.isFirstLoad = false
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
previousIndexPath = indexPath
collectionView.reloadItems(at: [indexPath])
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if previousIndexPath != indexPath {
collectionView.reloadItems(at: [indexPath])
guard let cell = collectionView.cellForItem(at: indexPath) as? PhotosCell else { fatalError("Couldn't return a photo cell") }
selectedImageView.image = cell.photoView.image
}
}
class PhotosCell: UICollectionViewCell {
let photoView = UIImageView()
var checkmarkBannerImageView: UIImageView?
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(photoView)
photoView.anchor(top: topAnchor, left: leadingAnchor, bottom: bottomAnchor, right: trailingAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupCheckmarkBanner() {
checkmarkBannerImageView = UIImageView(image: #imageLiteral(resourceName: "check").withRenderingMode(.alwaysTemplate))
checkmarkBannerImageView?.contentMode = .center
checkmarkBannerImageView?.contentScaleFactor = 4.0
checkmarkBannerImageView?.tintColor = .white
checkmarkBannerImageView?.setBottomBorder(for: .white)
checkmarkBannerImageView?.convertToCircle(value: 12)
photoView.addSubview(checkmarkBannerImageView!)
checkmarkBannerImageView?.anchor(top: photoView.topAnchor, left: nil, bottom: nil, right: photoView.trailingAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 24, height: 24)
}
func removeCheckmarkBanner() {
checkmarkBannerImageView?.removeFromSuperview()
checkmarkBannerImageView?.constraints.forEach{$0.isActive = false }
checkmarkBannerImageView = nil
}
}
to avoid unwanted content in reused cell, you can override the prepareForReuse() method and, in there, do all the setup work (e.g. hide the checkmark)
You have to save the indexpath of the selected cell (create a property and assign during the DidSelect and DidDescelect, and during cellForRow you check to see if the indexpath is the same, if yes, put the checkmark, if not remove the checkmark.
That way when the cell gets reused the ui setup will be refreshed.
Also, during deselect/select, do a reloadCellAtIndexPath/reloadItems only for the rows that changed to avoid reloading all the cells again.
EDIT:
Well, first thing I would change is, all the logic you have inside the block for photoManager.requestImage I would move outside as this is probably an async method.
Second, you are not setting up or removing the checkmark in every cell when you should since when it gets reused this method gets called.
This is just an example:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoCellReuseIdentifier, for: indexPath) as? PhotosCell else { fatalError("Couldn't return a photo cell") }
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.isNetworkAccessAllowed = true
requestOptions.deliveryMode = .highQualityFormat
photoManager.requestImage(for: photoAssets![indexPath.row], targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: { (image, nil) in
cell.photoView.image = image
if indexPath.row == 0 && self.isFirstLoad {
self.selectedImageView.image = image
}
})
if indexPath.row == 0 && self.isFirstLoad {
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: UICollectionViewScrollPosition.bottom)
self.isFirstLoad = false
}
if cell.isSelected { // dont remember if this is the correct method
cell.setupCheckmarkBanner()
} else {
cell.removeCheckmarkBanner()
}
return cell
}
I figured it out! It has to be done after dequeueing...
import UIKit
import Photos
class PhotosViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
let selectedImageView = UIImageView()
var photoAssets: PHFetchResult<PHAsset>?
let photoManager = PHImageManager.default()
var selectedIndexPath: IndexPath = IndexPath(row: 0, section: 0)
let collectionView: UICollectionView = {
let collectionViewFlowLayout = UICollectionViewFlowLayout()
collectionViewFlowLayout.sectionInset = UIEdgeInsetsMake(0, 2, 0, 2)
collectionViewFlowLayout.minimumInteritemSpacing = 0
collectionViewFlowLayout.minimumLineSpacing = 2
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
collectionView.backgroundColor = .white
return collectionView
}()
private let photoCellReuseIdentifier = "photoCell"
var photos = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(selectedImageView)
view.addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
fetchAssets()
selectedImageView.anchor(top: view.topAnchor, left: view.leadingAnchor, bottom: collectionView.topAnchor, right: view.trailingAnchor, paddingTop: 0, paddingLeft: 2, paddingBottom: 2, paddingRight: 2, width: 0, height: view.bounds.height/1.5)
collectionView.anchor(top: nil, left: view.leadingAnchor, bottom: view.bottomAnchor, right: view.trailingAnchor, paddingTop: 5, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
collectionView.register(PhotosCell.self, forCellWithReuseIdentifier: photoCellReuseIdentifier)
}
private func fetchAssets() {
guard let cameraRollPhotoCollection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil).firstObject else { return }
let assetOptions = PHFetchOptions()
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
assetOptions.sortDescriptors = [sortDescriptor]
photoAssets = PHAsset.fetchAssets(in: cameraRollPhotoCollection, options: assetOptions)
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.isNetworkAccessAllowed = true
requestOptions.deliveryMode = .highQualityFormat
guard let photoAssets = photoAssets else { return }
let photoAssetsCount = photoAssets.count - 1
for assetIndex in 0..<photoAssetsCount {
photoManager.requestImage(for: photoAssets[assetIndex], targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: { (image, nil) in
guard let image = image else { return }
self.photos.append(image)
})
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoCellReuseIdentifier, for: indexPath) as? PhotosCell else { fatalError("Couldn't return a photo cell") }
cell.photoView.image = photos[indexPath.row]
if indexPath == selectedIndexPath {
cell.setupCheckmarkBanner()
}
else {
cell.removeCheckmarkBanner()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.bounds.size.width/3 - 3
let height = view.bounds.size.height/5
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if collectionView.indexPathsForVisibleItems.contains(indexPath) {
guard let deselectedCell = collectionView.cellForItem(at: indexPath) as? PhotosCell else { fatalError("Couldn't return a photo cell") }
deselectedCell.removeCheckmarkBanner()
}
else {
collectionView.deselectItem(at: indexPath, animated: false)
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let selectedCell = collectionView.cellForItem(at: indexPath) as? PhotosCell else { fatalError("Couldn't return a photo cell") }
selectedIndexPath = indexPath
selectedCell.setupCheckmarkBanner()
}
}
extension PhotosViewController: ImageDismissalDelegate {
func sendProfilePictureImage(picture: UIImage) {
}
}

CollectionView return the same images twice

When I request the images and load them to my collection View, the request return low resolution images and high resolution images at the same time.
As you can see the same images are returned twice with low and hight resolution in my collectionView.
Code:
var imageArray = [UIImage]()
let imgManager = PHImageManager.default()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
grabPhoto()
}
func grabPhoto(){
let imageSize = CGSize(width: 800, height: 800)
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) as? PHFetchResult {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count {
imgManager.requestImage(for: fetchResult.object(at: i), targetSize: imageSize, contentMode: .aspectFill , options: nil, resultHandler: { (image, error) in
self.imageArray.append(image!)
self.photoImageView.image = self.imageArray.first
self.collectionView.reloadData()
print("Result Size Is \(image?.size)")
})
}
}
} else {
print("you got no Photos!")
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UploadPhotoCell", for: indexPath)
let imageView = cell.viewWithTag(1) as! UIImageView
imageView.image = imageArray[indexPath.row]
return cell
}