This source code is a photo app like the iPhone's photo app.
When you launch the app, each Asset that is a CollectionViewCell is shown.
What I would like to ask is the ability to select and delete an image asset. If you look at the iPhone photo app, you can press the select button to select photos and delete and share selected photos. You can choose as many photos as you want rather than just one. I have implemented #IBAction selectButtonPressed.
class PhotoCollectionViewController: UICollectionViewController {
#IBOutlet weak var sortButton: UIBarButtonItem!
#IBOutlet weak var selectButton: UIBarButtonItem!
#IBOutlet weak var actionButton: UIBarButtonItem!
#IBOutlet weak var trashButton: UIBarButtonItem!
// MARK:- Properties
var fetchResult: PHFetchResult<PHAsset>? {
didSet {
OperationQueue.main.addOperation {
self.collectionView?.reloadSections(IndexSet(0...0))
}
}
}
var assetCollection: PHAssetCollection?
// MARK:- Privates
private let cellReuseIdentifier: String = "photoCell"
private lazy var cachingImageManager: PHCachingImageManager = {
return PHCachingImageManager()
}()
// MARK:- Life Cycle
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
#IBAction func sortButtonPressed(_ sender: UIBarButtonItem) {
let fetchOptions: PHFetchOptions = PHFetchOptions()
if (self.sortButton.title == "In the past") {
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "modificationDate",
ascending: false)]
self.fetchResult = PHAsset.fetchAssets(in: assetCollection!, options: fetchOptions )
self.sortButton.title = "The latest"
} else if (self.sortButton.title == "The latest") {
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: true)]
self.fetchResult = PHAsset.fetchAssets(in: assetCollection!, options: fetchOptions )
self.sortButton.title = "In the past"
}
}
#IBAction func seletButtonPressed(_ sender: Any) {
if (self.sortButton.isEnabled == true) {
self.sortButton.isEnabled = false
self.actionButton.isEnabled = true
self.trashButton.isEnabled = true
} else if (self.sortButton.isEnabled == false) {
self.sortButton.isEnabled = true
self.actionButton.isEnabled = false
self.trashButton.isEnabled = false
}
PHPhotoLibrary.shared().performChanges({
//Delete Photo
PHAssetChangeRequest.deleteAssets(self.fetchResult!)
},
completionHandler: {(success, error)in
NSLog("\nDeleted Image -> %#", (success ? "Success":"Error!"))
if(success){
}else{
print("Error: \(error)")
}
})
}
}
extension PhotoCollectionViewController {
private func configureCell(_ cell: PhotoCollectionViewCell,
collectionView: UICollectionView,
indexPath: IndexPath) {
guard let asset: PHAsset = self.fetchResult?.object(at: indexPath.item) else { return }
let manager: PHCachingImageManager = self.cachingImageManager
let handler: (UIImage?, [AnyHashable:Any]?) -> Void = { image, _ in
let cellAtIndex: UICollectionViewCell? = collectionView.cellForItem(at: indexPath)
guard let cell: PhotoCollectionViewCell = cellAtIndex as? PhotoCollectionViewCell
else { return }
cell.imageView.image = image
}
manager.requestImage(for: asset,
targetSize: CGSize(width: 100, height: 100),
contentMode: PHImageContentMode.aspectFill,
options: nil,
resultHandler: handler)
}
}
// MARK:- UICollectionViewDataSource
extension PhotoCollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return self.fetchResult?.count ?? 0
}
}
extension PhotoCollectionViewController {
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PhotoCollectionViewCell
cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier,
for: indexPath) as! PhotoCollectionViewCell
return cell
}
override func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
guard let cell: PhotoCollectionViewCell = cell as? PhotoCollectionViewCell else {
return
}
self.configureCell(cell, collectionView: collectionView, indexPath: indexPath)
}
}
// MARK:- UICollectionViewDelegateFlowLayout
extension PhotoCollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
guard let flowLayout: UICollectionViewFlowLayout =
self.collectionViewLayout as? UICollectionViewFlowLayout else { return CGSize.zero}
let numberOfCellsInRow: CGFloat = 4
let viewSize: CGSize = self.view.frame.size
let sectionInset: UIEdgeInsets = flowLayout.sectionInset
let interitemSpace: CGFloat = flowLayout.minimumInteritemSpacing * (numberOfCellsInRow - 1)
var itemWidth: CGFloat
itemWidth = viewSize.width - sectionInset.left - sectionInset.right - interitemSpace
itemWidth /= numberOfCellsInRow
let itemSize = CGSize(width: itemWidth, height: itemWidth)
return itemSize
}
}
extension PhotoCollectionViewController {
private func updateCollectionView(with changes: PHFetchResultChangeDetails<PHAsset>) {
guard let collectionView = self.collectionView else { return }
// 업데이트는 삭제, 삽입, 다시 불러오기, 이동 순으로 진행합니다
if let removed: IndexSet = changes.removedIndexes, removed.count > 0 {
collectionView.deleteItems(at: removed.map({
IndexPath(item: $0, section: 0)
}))
}
if let inserted: IndexSet = changes.insertedIndexes, inserted.count > 0 {
collectionView.insertItems(at: inserted.map({
IndexPath(item: $0, section: 0)
}))
}
if let changed: IndexSet = changes.changedIndexes, changed.count > 0 {
collectionView.reloadItems(at: changed.map({
IndexPath(item: $0, section: 0)
}))
}
changes.enumerateMoves { fromIndex, toIndex in
collectionView.moveItem(at: IndexPath(item: fromIndex, section: 0),
to: IndexPath(item: toIndex, section: 0))
}
}
}
// MARK:- PHPhotoLibraryChangeObserver
extension PhotoCollectionViewController: PHPhotoLibraryChangeObserver {
private func resetCachedAssets() {
self.cachingImageManager.stopCachingImagesForAllAssets()
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let fetchResult: PHFetchResult<PHAsset> = self.fetchResult
else { return }
guard let changes: PHFetchResultChangeDetails<PHAsset> =
changeInstance.changeDetails(for: fetchResult)
else { return }
DispatchQueue.main.sync {
self.resetCachedAssets()
self.fetchResult = changes.fetchResultAfterChanges
if changes.hasIncrementalChanges {
self.updateCollectionView(with: changes)
} else {
self.collectionView?.reloadSections(IndexSet(0...0))
}
}
}
}
extension PhotoCollectionViewController {
// MARK:- Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
PHPhotoLibrary.shared().register(self)
self.sortButton.title = "In the past"
self.actionButton.isEnabled = false
self.trashButton.isEnabled = false
}
}
AssetCollection can be selected by clicking the select barButtonItem in the upper right corner of the screen, and I want to delete or share selected pictures.
You can perfore delete action on phAsset as below:
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
//Delete Photo
PHAssetChangeRequest.deleteAssets(delShotsAsset)
},
completionHandler: {(success, error)in
NSLog("\nDeleted Image -> %#", (success ? "Success":"Error!"))
if(success){
}else{
println("Error: \(error)")
}
})
Related
I have no idea why this is happening, I had a different Xcode project with this exact same collection view just to test things out and make it and I've finished making it and it works very well in that project. Then I moved all the code and items over to my main Xcode project and the collection view just isn't showing in my view controller when I play the app.
How do you debug collection views? The two: playerCollect?.cellForItem(at: path) and playerCollect.indexPathForItem(at: p) both return nil if that's an issue? I don't know. I might've not connected everything back together once I moved it over to my main project, but yet again how would you find that out? Here's my full code for the viewController containing the UICollectionView:
import UIKit
import AVKit
import Firebase
import MMPlayerView
class HomeViewController: UIViewController {
var offsetObservation: NSKeyValueObservation?
lazy var mmPlayerLayer: MMPlayerLayer = {
let l = MMPlayerLayer()
l.cacheType = .memory(count: 5)
l.coverFitType = .fitToPlayerView
l.videoGravity = AVLayerVideoGravity.resizeAspect
l.replace(cover: CoverA.instantiateFromNib())
l.repeatWhenEnd = true
return l
}()
#IBOutlet weak var playerCollect: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// remove previous download fails file
MMPlayerDownloader.cleanTmpFile()
self.navigationController?.mmPlayerTransition.push.pass(setting: { (_) in
})
offsetObservation = playerCollect.observe(\.contentOffset, options: [.new]) { [weak self] (_, value) in
guard let self = self, self.presentedViewController == nil else {return}
NSObject.cancelPreviousPerformRequests(withTarget: self)
self.perform(#selector(self.startLoading), with: nil, afterDelay: 0.2)
}
playerCollect.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right:0)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
self?.updateByContentOffset()
self?.startLoading()
}
mmPlayerLayer.getStatusBlock { [weak self] (status) in
switch status {
case .failed(let err):
let alert = UIAlertController(title: "err", message: err.description, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
case .ready:
print("Ready to Play")
case .playing:
print("Playing")
case .pause:
print("Pause")
case .end:
print("End")
default: break
}
}
mmPlayerLayer.getOrientationChange { (status) in
print("Player OrientationChange \(status)")
}
}
deinit {
offsetObservation?.invalidate()
offsetObservation = nil
print("ViewController deinit")
}
#IBAction func profileButtonTap(_ sender: Any) {
let uid = (Auth.auth().currentUser?.uid)!
let Splash = SpalshScreenViewController()
Splash.GetProfilePicture(uid: uid)
Splash.GetUsername(uid: uid)
Splash.GetName(uid: uid)
Splash.GetClipsNumber(uid: uid)
Splash.GetFollowersNumber(uid: uid)
Splash.GetFollowingsNumber(uid: uid)
performSegue(withIdentifier: "showProfile", sender: nil)
}
}
extension HomeViewController: MMPlayerFromProtocol {
func backReplaceSuperView(original: UIView?) -> UIView? {
guard let path = self.findCurrentPath(),
let cell = self.findCurrentCell(path: path) as? PlayerCell else {
return original
}
return cell.imgView
}
// add layer to temp view and pass to another controller
var passPlayer: MMPlayerLayer {
return self.mmPlayerLayer
}
func transitionWillStart() {
}
// show cell.image
func transitionCompleted() {
self.updateByContentOffset()
self.startLoading()
}
}
extension HomeViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let m = min(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
return CGSize(width: m, height: m*0.75)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
DispatchQueue.main.async { [unowned self] in
if self.presentedViewController != nil || self.mmPlayerLayer.isShrink == true {
//self.playerCollect.scrollToItem(at: indexPath, at: .centeredVertically, animated: true)
//self.updateDetail(at: indexPath)
} else {
self.presentDetail(at: indexPath)
}
}
}
fileprivate func updateByContentOffset() {
if mmPlayerLayer.isShrink {
return
}
if let path = findCurrentPath(),
self.presentedViewController == nil {
self.updateCell(at: path)
//Demo SubTitle
if path.row == 0, self.mmPlayerLayer.subtitleSetting.subtitleType == nil {
let subtitleStr = Bundle.main.path(forResource: "srtDemo", ofType: "srt")!
if let str = try? String.init(contentsOfFile: subtitleStr) {
self.mmPlayerLayer.subtitleSetting.subtitleType = .srt(info: str)
self.mmPlayerLayer.subtitleSetting.defaultTextColor = .red
self.mmPlayerLayer.subtitleSetting.defaultFont = UIFont.boldSystemFont(ofSize: 20)
}
}
}
}
fileprivate func presentDetail(at indexPath: IndexPath) {
self.updateCell(at: indexPath)
mmPlayerLayer.resume()
if let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DetailViewController") as? DetailViewController {
vc.data = DemoSource.shared.demoData[indexPath.row]
self.present(vc, animated: true, completion: nil)
}
}
fileprivate func updateCell(at indexPath: IndexPath) {
if let cell = playerCollect.cellForItem(at: indexPath) as? PlayerCell, let playURL = cell.data?.play_Url {
// this thumb use when transition start and your video dosent start
mmPlayerLayer.thumbImageView.image = cell.imgView.image
// set video where to play
mmPlayerLayer.playView = cell.imgView
mmPlayerLayer.set(url: playURL)
}
}
#objc fileprivate func startLoading() {
self.updateByContentOffset()
if self.presentedViewController != nil {
return
}
// start loading video
mmPlayerLayer.resume()
}
private func findCurrentPath() -> IndexPath? {
let p = CGPoint(x: playerCollect.contentOffset.x + playerCollect.frame.width/2, y: playerCollect.frame.height/2)
print(playerCollect.indexPathForItem(at: p))
return playerCollect.indexPathForItem(at: p)
}
private func findCurrentCell(path: IndexPath) -> UICollectionViewCell? {
print(playerCollect?.cellForItem(at: path))
return playerCollect?.cellForItem(at: path)
}
}
extension HomeViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return DemoSource.shared.demoData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlayerCell", for: indexPath) as? PlayerCell {
cell.data = DemoSource.shared.demoData[indexPath.row]
return cell
}
return UICollectionViewCell()
}
}
Did you give the collection view's delegate and data source at this view controller?
self.playerCollect.delegate = self
self.playerCollect.dataSource = self
and add:
self.playerCollect.reloadData()
Update your code as below.
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.playerCollect.delegate = self
self.playerCollect.dataSource = self
}
}
extension HomeViewController: UICollectionViewDataSource, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return DemoSource.shared.demoData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlayerCell", for: indexPath) as? PlayerCell {
cell.data = DemoSource.shared.demoData[indexPath.row]
return cell
}
return UICollectionViewCell()
}
}
I am asking the same question as here
I do not understand how to implement to solution.
I have tried the following
fileprivate func fetchPhotos(indexSet: IndexSet) {
let allPhotos = PHAsset.fetchAssets(with: .image, options: assetsFetchOptions())
DispatchQueue.global(qos: .background).async {
allPhotos.enumerateObjects(at: indexSet, options: NSEnumerationOptions.concurrent, using: { (asset, count, stop) in
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: 200, height: 200)
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
self.images.append(image)
self.assets.append(asset)
if self.selectedImage == nil {
self.selectedImage = image
}
}
DispatchQueue.main.async {
self.collectionView.reloadData()
self.hud.dismiss()
}
})
})
}
}
In cellForItemAt I tried doubling the indexes so the next 10 would load. The result i got was a never ending repetition of the first 10 posts.
Can someone please show the proper way to use this.
I tried to do what you want. I couldn't encapsulate it inside one function, coz it requires a few public variables, so here is the code for a UIViewController that has a UICollectionView which loads images by a page of 10 and when scrolled to the last cell it loads next 10 images and so on.
import UIKit
import Photos
import PhotosUI
class ImagesViewController: UIViewController ,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
var assets = [PHAsset]()
var images = [UIImage]()
let page = 10
var beginIndex = 0
var endIndex = 9
var allPhotos : PHFetchResult<PHAsset>?
var loading = false
var hasNextPage = false
#IBOutlet weak var collectionView : UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let options = PHFetchOptions()
options.includeHiddenAssets = true
allPhotos = PHAsset.fetchAssets(with: .image, options: options)
getImages()
}
func getImages(){
endIndex = beginIndex + (page - 1)
if endIndex > allPhotos!.count {
endIndex = allPhotos!.count - 1
}
let arr = Array(beginIndex...endIndex)
let indexSet = IndexSet(arr)
fetchPhotos(indexSet: indexSet)
}
fileprivate func fetchPhotos(indexSet: IndexSet) {
if allPhotos!.count == self.images.count {
self.hasNextPage = false
self.loading = false
return
}
self.loading = true
DispatchQueue.global(qos: .background).async { [weak self] in
self?.allPhotos?.enumerateObjects(at: indexSet, options: NSEnumerationOptions.concurrent, using: { (asset, count, stop) in
guard let weakSelf = self else {
return
}
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: weakSelf.view.frame.size.height - 20, height: 250)
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
weakSelf.images.append(image)
weakSelf.assets.append(asset)
}
})
if weakSelf.images.count - 1 == indexSet.last! {
print("last element")
weakSelf.loading = false
weakSelf.hasNextPage = weakSelf.images.count != weakSelf.allPhotos!.count
weakSelf.beginIndex = weakSelf.images.count
DispatchQueue.main.async {
weakSelf.collectionView.reloadData()
}
}
})
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let imgView = cell.viewWithTag(1) as! UIImageView
imgView.image = self.images[indexPath.row]
if self.hasNextPage && !loading && indexPath.row == self.images.count - 1 {
getImages()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:collectionView.frame.size.width - 20,height: 250)
}
}
Refinement and issues Resolved , Now below code Working fine
import Photos
import PhotosUI
class PhotoGalleryPaginationViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate {
var assets = [PHAsset]()
var images = [UIImage]()
let page = 10
var beginIndex = 0
var endIndex = 9
var allPhotos : PHFetchResult<PHAsset>?
var loading = false
var hasNextPage = false
#IBOutlet weak var collectionView : UICollectionView!
// MARK :- LIFE CYCLE
override func viewDidLoad() {
super.viewDidLoad()
// Cell Register
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
// Fetch Gallery
let options = PHFetchOptions()
options.includeHiddenAssets = true
allPhotos = PHAsset.fetchAssets(with: .image, options: options)
getImages()
// Do any additional setup after loading the view.
}
// MARK :- Methods
func getImages(){
endIndex = beginIndex + (page - 1)
if endIndex > allPhotos!.count {
endIndex = allPhotos!.count - 1
}
if endIndex > beginIndex {
let arr = Array(beginIndex...endIndex)
let indexSet = IndexSet(arr)
fetchPhotos(indexSet: indexSet)
}
}
fileprivate func fetchPhotos(indexSet: IndexSet) {
if allPhotos!.count == self.images.count {
self.hasNextPage = false
self.loading = false
return
}
self.loading = true
let targetSize = CGSize(width: 200, height: 200)
DispatchQueue.global(qos: .background).async { [weak self] in
self?.allPhotos?.enumerateObjects(at: indexSet, options: NSEnumerationOptions.concurrent, using: { (asset, count, stop) in
guard let weakSelf = self else {
return
}
let imageManager = PHImageManager.default()
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
weakSelf.images.append(image)
weakSelf.assets.append(asset)
}
})
if weakSelf.images.count - 1 == indexSet.last! {
print("last element")
weakSelf.loading = false
weakSelf.hasNextPage = weakSelf.images.count != weakSelf.allPhotos!.count
weakSelf.beginIndex = weakSelf.images.count
DispatchQueue.main.async {
weakSelf.collectionView.reloadData()
}
}
})
}
}
// MARK :- CollectionView DataSource and Delegate
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("image Count = \(images.count)")
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let imgView = UIImageView()
imgView.frame = CGRect(x: 0, y: 0, width: 150, height: 150)
imgView.image = self.images[indexPath.row]
cell.contentView.addSubview(imgView)
cell.clipsToBounds = true
// Fetch new Images
if self.hasNextPage && !loading && indexPath.row == self.images.count - 1 {
getImages()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:collectionView.frame.width/2,height: 150)
}
}
I'm trying to implement two UICollectionView in the same UIViewController. However, It's going wrong, when I run the app just one UICollectionView shows up, the other just keeps in blank. I've trying many ways to fix that, but I always end up failing.
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet weak var collectionViewBanner: UICollectionView!
var dataSource: [Content] = [Content]()
var dataBanner: [Banner] = [Banner]()
override func viewDidLoad() {
super.viewDidLoad()
getBanner { (data) in
DispatchQueue.main.async {
self.dataBanner = data
self.collectionViewBanner.reloadData()
}
}
getAudiobooksAPI { (data) in
DispatchQueue.main.async {
self.dataSource = data
self.collectionView.reloadData()
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if self.collectionView.tag == 1 {
return self.dataSource.count
} else {
return self.dataBanner.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (collectionView == self.collectionView) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionViewCell
let content = self.dataSource[indexPath.item]
cell.bookLabel.text = content.descricao
cell.bookImage.setImage(url: content.urlImagem, placeholder: "")
return cell
} else if (collectionView == self.collectionViewBanner) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCellBanner", for: indexPath) as! CollectionViewCell
let content = self.dataBanner[indexPath.item]
cell.bannerImage.setImage(url: content.urlImagem, placeholder: "")
return cell
}
return UICollectionViewCell()
}
}
extension UIImageView {
func setImage(url : String, placeholder: String, callback : (() -> Void)? = nil){
self.image = UIImage(named: "no-photo")
URLSession.shared.dataTask(with: NSURL(string: url)! as URL, completionHandler: { (data, response, error) -> Void in
guard error == nil else {
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
if let callback = callback{
callback()
}
})
}).resume()
}
}
The other UICollectionView should appear above this one. What I'm doing wrong?
App Running Image
[EDIT]
I'm debugging the code, and I put a breakpoint on these lines. So, what happened was that the compiler just read the "if", doesn't go into the "else".
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if self.collectionView.tag == 1 {
return self.dataSource.count
} else {
return self.dataBanner.count
}
}
Please Use My code it will solve your Problem
First change
firstchange in collectionViewBanner.reloadData
Second Change
Just check NumberofiteminSection method
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet weak var collectionViewBanner: UICollectionView!
var dataSource: [Content] = [Content]()
var dataBanner: [Banner] = [Banner]()
override func viewDidLoad() {
super.viewDidLoad()
getBanner { (data) in
DispatchQueue.main.async {
self.dataBanner = data
self.collectionViewBanner.reloadData()
}
}
getAudiobooksAPI { (data) in
DispatchQueue.main.async {
self.dataSource = data
self.collectionView.reloadData()
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (collectionView == self.collectionView) {
return self.dataSource.count
}else{
return self.dataBanner.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (collectionView == self.collectionView) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionViewCell
let content = self.dataSource[indexPath.item]
cell.bookLabel.text = content.descricao
cell.bookImage.setImage(url: content.urlImagem, placeholder: "")
return cell
}else if (collectionView == self.collectionViewBanner) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCellBanner", for: indexPath) as! CollectionViewCell
let content = self.dataBanner[indexPath.item]
cell.bannerImage.setImage(url: content.urlImagem, placeholder: "")
return cell
}
return UICollectionViewCell()
}
}
extension UIImageView{
func setImage(url : String, placeholder: String, callback : (() -> Void)? = nil){
self.image = UIImage(named: "no-photo")
URLSession.shared.dataTask(with: NSURL(string: url)! as URL, completionHandler: { (data, response, error) -> Void in
guard error == nil else{
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
if let callback = callback{
callback()
}
})
}).resume()
}
}
getBanner { (data) in
DispatchQueue.main.async {
self.dataBanner = data
self.collectionViewBanner.reloadData()
}
}
getAudiobooksAPI { (data) in
DispatchQueue.main.async {
self.dataSource = data
self.collectionView.reloadData()
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (collectionView == self.collectionView) {
return self.dataSource.count
}else if (collectionView == self.collectionViewBanner) {
return self.dataBanner.count
} else {
return nil
}
}
You are forget to reload collectionViewBanner. just
collectionViewBanner.reloadData()
My didSelectItemAt method is not being called and nothing is being printed into the console. I have user interaction turned on and I still can not get it to print out anything. I am not sure if my custom PinterestStyle Layout is causing this or if I am missing something. The ultimate goal would be to segue into a detail view controller showing the profile page of the cell selected. I will do that using prepareForSegue however I still can't even get it to print out the name of the cell when tapped.
class PagesCollectionViewController: UICollectionViewController, firebaseHelperDelegate {
var storageRef: StorageReference!{
return Storage.storage().reference()
}
var usersList = [String]()
var authService : FirebaseHelper!
var userArray : [Users] = []
var images: [UIImage] = []
var names: [String] = []
override func viewWillAppear(_ animated: Bool) {
if Global.Location != "" && Global.Location != nil
{
usersList = Global.usersListSent
print(usersList)
self.authService.ListOfUserByLocation(locationName: Global.Location, type: .ListByLocation)
}
}
override func viewDidLoad() {
self.collectionView?.allowsSelection = true
self.collectionView?.isUserInteractionEnabled = true
super.viewDidLoad()
self.authService = FirebaseHelper(viewController: self)
self.authService.delegate = self
setupCollectionViewInsets()
setupLayout()
}
private func setupCollectionViewInsets() {
collectionView!.backgroundColor = .white
collectionView!.contentInset = UIEdgeInsets(
top: 20,
left: 5,
bottom: 49,
right: 5
)
}
private func setupLayout() {
let layout: PinterestLayout = {
if let layout = collectionViewLayout as? PinterestLayout {
return layout
}
let layout = PinterestLayout()
collectionView?.collectionViewLayout = layout
return layout
}()
layout.delegate = self
layout.cellPadding = 5
layout.numberOfColumns = 2
}
func firebaseCallCompleted(data: AnyObject?, isSuccess: Bool, error: Error?, type: FirebaseCallType) {
if(type == .ListByLocation) {
if(isSuccess) {
self.userArray.removeAll()
self.images.removeAll()
self.images.removeAll()
if(data != nil) {
let dataDict = data as! NSDictionary
let keyArray = dataDict.allKeys
for i in 0 ..< keyArray.count {
var dict = NSDictionary()
dict = dataDict.object(forKey: keyArray[i]) as! NSDictionary
self.userArray.append(Users.init(data: dict))
}
}
self.collectionView?.reloadData()
}
else {
print(error?.localizedDescription)
SVProgressHUD.dismiss()
}
}
}
}
extension PagesCollectionViewController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return userArray.count
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(userArray[indexPath.row].name)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "PagesCollectionViewCell",
for: indexPath) as! PagesCollectionViewCell
cell.nameLabel.text = userArray[indexPath.row].name
if let imageOld = URL(string: userArray[indexPath.row].photoURL){
cell.photo.sd_setImage(
with: imageOld,
placeholderImage: nil,
options: [.continueInBackground, .progressiveDownload]
)
}
return cell
}
}
extension PagesCollectionViewController : PinterestLayoutDelegate {
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
var image: UIImage?
let url = URL(string: userArray[indexPath.row].photoURL)
let data = try? Data(contentsOf: url!)
image = UIImage(data: data!)
return (image?.height(forWidth: withWidth))!
}
func collectionView(collectionView: UICollectionView,
heightForAnnotationAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
return 30
}
}
Check 2 conditions:-
Make sure you have set delegate to UICollectionView
Make sure Content in PageCollectionCell like image having no user interaction enabled. If image user interaction is enabled then didSelectItemAt will not call.
as Manish Mahajan said a quick fix would be:
in cellForItemAt func set contentView as not clickable
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.contentView.isUserInteractionEnabled = false
return cell
}
Hi I am using KDDragAndDropCollectionView for drag and drop feature between two three different collectionView. Everything is working fine, but I am not able to restrict the movement for particular case. I have three collectionView. The user can drop from A to B and B to C. But He cannot drah and drop from A to C or C to B or B to A.
Here is my code.
import SlideMenuControllerSwift
class MainViewController: NavigationBarViewController,KDDragAndDropCollectionViewDataSource {
#IBOutlet weak var inProgressView: UIView!
#IBOutlet weak var doneview: UIView!
#IBOutlet weak var toDoView: UIView!
#IBOutlet weak var doneCollectionView: UICollectionView!
#IBOutlet weak var inProgressCollectionView: UICollectionView!
#IBOutlet weak var toDoCollectionView: UICollectionView!
var drop: UIDropDown!
var toDoDataArray = [String]()
var inProgressDataArray = [String]()
var doneDataArray = [String]()
var dragAndDropManager : KDDragAndDropManager?
//MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
toDoDataArray.append("A")
toDoDataArray.append("B")
toDoDataArray.append("C")
inProgressDataArray.append("D")
doneDataArray.append("B")
doneDataArray.append("C")
// Do any additional setup after loading the view.
self.setUp()
self.setupDropDown()
self.dragAndDropManager = KDDragAndDropManager(canvas: self.view, collectionViews: [toDoCollectionView, inProgressCollectionView,doneCollectionView])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
}
//MARK: - Private Method
func setupDropDown() {
drop = UIDropDown(frame: CGRect(x: 24, y: 80, width: 200, height: 40))
//drop.center = CGPoint(x: self.view.frame.midX, y: self.view.frame.midY)
drop.placeholder = "Select Month"
drop.options = ["Weekly", "Monthly", "Bi-Annual", "Annual"]
drop.didSelect { (option, index) in
self.drop.placeholder = option
print("You just select: \(option) at index: \(index)")
}
self.view.addSubview(drop)
}
func setUp()
{
//Setup Navigation Bar
self.menuIconImage = #imageLiteral(resourceName: "hamIco")
self.setNavigationBarButtonItem()
self.setNavigationBarItem()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
//MARK: - Navigation Bar Button Action Method
//MARK: - Button Action Methods
/**
Button Action method. Gets called when the left navigation item
Parameters: sender - the button on which the event occurred
**/
#IBAction func leftBtnAction(sender: UIButton) {
self.toggleLeft()
}
// MARK: - UITableView DataSource abd Delegate
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("Step 1 called")
if collectionView.tag == 0 {
return toDoDataArray.count
}
else if collectionView.tag == 1 {
return inProgressDataArray.count
}
return doneDataArray.count
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("Step 2 called")
if collectionView.tag == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TO_DO_COLLECTION_CELL_ID, for: indexPath) as! ToDoCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
else if collectionView.tag == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: IN_PROGRESS_COLLECTION_CELL_ID, for: indexPath) as! InProgressCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DONE_COLLECTION_Cell_ID, for: indexPath) as! DoneCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
}
// MARK : KDDragAndDropCollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, dataItemForIndexPath indexPath: IndexPath) -> AnyObject {
print("Step 3 called")
if collectionView.tag == 0 {
return toDoDataArray[indexPath.item] as AnyObject
}
else if collectionView.tag == 1 {
return inProgressDataArray[indexPath.item] as AnyObject
}
return doneDataArray[indexPath.item] as AnyObject
//return data[collectionView.tag][indexPath.item]
}
func collectionView(_ collectionView: UICollectionView, insertDataItem dataItem : AnyObject, atIndexPath indexPath: IndexPath) -> Void {
print("Step 4 called")
if collectionView.tag == 0 {
if let di = dataItem as? String {
toDoDataArray.insert(di, at: indexPath.item)
//data[collectionView.tag].insert(di, at: indexPath.item)
}
}
else if collectionView.tag == 1 {
if let di = dataItem as? String {
inProgressDataArray.insert(di, at: indexPath.item)
//data[collectionView.tag].insert(di, at: indexPath.item)
}
}
else{
if let di = dataItem as? String {
doneDataArray.insert(di, at: indexPath.item)
}
}
}
func collectionView(_ collectionView: UICollectionView, deleteDataItemAtIndexPath indexPath : IndexPath) -> Void {
print("Step 5 called")
if collectionView.tag == 0 {
toDoDataArray.remove(at: indexPath.item)
}
else if collectionView.tag == 1 {
inProgressDataArray.remove(at: indexPath.item)
}
else{
doneDataArray.remove(at: indexPath.item)
}
}
func collectionView(_ collectionView: UICollectionView, moveDataItemFromIndexPath from: IndexPath, toIndexPath to : IndexPath) -> Void {
print("Step 6 called")
if collectionView.tag == 0 {
let fromDataItem: String = toDoDataArray[from.item]
toDoDataArray.remove(at: from.item)
toDoDataArray.insert(fromDataItem, at: to.item)
}
else if collectionView.tag == 1 {
let fromDataItem: String = inProgressDataArray[from.item]
inProgressDataArray.remove(at: from.item)
inProgressDataArray.insert(fromDataItem, at: to.item)
}
else{
let fromDataItem: String = doneDataArray[from.item]
doneDataArray.remove(at: from.item)
doneDataArray.insert(fromDataItem, at: to.item)
}
}
func collectionView(_ collectionView: UICollectionView, indexPathForDataItem dataItem: AnyObject) -> IndexPath? {
print("Step 7 called")
if collectionView.tag == 0 {
if let candidate : String = dataItem as? String {
for item : String in toDoDataArray {
if candidate == item {
let position = toDoDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
else if collectionView.tag == 1 {
if let candidate : String = dataItem as? String {
for item : String in inProgressDataArray {
if candidate == item {
let position = inProgressDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
else{
if let candidate : String = dataItem as? String {
for item : String in doneDataArray {
if candidate == item {
let position = doneDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
return nil
}
}
//MARK: - Extension written for Slide the Menu
extension MainViewController : SlideMenuControllerDelegate {
func leftWillOpen() {
print("SlideMenuControllerDelegate: leftWillOpen")
}
func leftDidOpen() {
print("SlideMenuControllerDelegate: leftDidOpen")
}
func leftWillClose() {
print("SlideMenuControllerDelegate: leftWillClose")
}
func leftDidClose() {
print("SlideMenuControllerDelegate: leftDidClose")
}
func rightWillOpen() {
print("SlideMenuControllerDelegate: rightWillOpen")
}
func rightDidOpen() {
print("SlideMenuControllerDelegate: rightDidOpen")
}
func rightWillClose() {
print("SlideMenuControllerDelegate: rightWillClose")
}
func rightDidClose() {
print("SlideMenuControllerDelegate: rightDidClose")
}
}