Passing data from CollectionVewCell to another view controller - swift

Let's say we have CollectionViewController with Cells
So, my goal is to send the data from the active CollectionViewControllerCell (let's say i want to pass the category name) to another controller after click action on that CELL. So, how can i pass data from
import UIKit
class CategoriesViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var activeCategoryItemTitle:String = ""
var categories = Category.fetchCategories()
let cellScale: CGFloat = 0.7
override func viewDidLoad() {
super.viewDidLoad()
let screenSize = UIScreen.main.bounds.size
let cellWidth = floor(screenSize.width * cellScale)
let cellHeight = floor(screenSize.height * cellScale)
let insetX = (view.bounds.width - cellWidth) / 2
let insetY = (view.bounds.height - cellHeight) / 2
let layout = collectionView!.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width:cellWidth,height:cellHeight)
// collectionView.contentInset = UIEdgeInsets(top:insetY,left:insetX,bottom:insetY,right:insetX)
print(insetY)
collectionView.dataSource = self
collectionView.delegate = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC: ProductsViewController = segue.destination as! ProductsViewController
destinationVC.titleOfCategory = self.activeCategoryItemTitle
// let product = ProductsViewController()
// product.titleOfCategory = "asd"
}
}
// MARK UICollectionViewDataSource
extension CategoriesViewController:
UICollectionViewDataSource
{
func numberOfSections(in collectionView: UICollectionView) ->
Int {
return 1
}
func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int) -> Int {
return categories.count
}
func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier: "CategoriesCollectionViewCell",for:indexPath) as!
CategoriesCollectionViewCell
let category = categories[indexPath.item]
cell.category = category
return cell
}
}
extension CategoriesViewController: UIScrollViewDelegate, UICollectionViewDelegate
{
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let layout = self.collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
let cellWidthincludingSpacing = layout.itemSize.width+layout.minimumLineSpacing
var offset = targetContentOffset.pointee
let index = (offset.x + scrollView.contentInset.left) / cellWidthincludingSpacing
let roundedIndex = round(index)
offset = CGPoint(x: roundedIndex*cellWidthincludingSpacing-scrollView.contentInset.left, y: scrollView.contentInset.top)
targetContentOffset.pointee = offset
}
}
and second one is my products list controller, that must receive category name and put inside view
//
import UIKit
class ProductsViewController: UIViewController {
#IBOutlet weak var categoryTitle: UILabel!
var titleOfCategory:String = ""
#IBOutlet weak var collectionView: UICollectionView!
var products = Product.fetchProducts()
// let cellScale: CGFloat = 0.63
override func viewDidLoad() {
super.viewDidLoad()
categoryTitle.text = titleOfCategory
// let screenSize = UIScreen.main.bounds.size
// let cellWidth = floor(screenSize.width * cellScale)
// let cellHeight = floor(screenSize.height * cellScale)
// let insetX = (view.bounds.width - cellWidth) / 2
// let insetY = (view.bounds.height - cellHeight) / 2
// let layout = collectionView!.collectionViewLayout as! UICollectionViewFlowLayout
// layout.itemSize = CGSize(width:cellWidth,height:cellHeight)
// collectionView.contentInset = UIEdgeInsets(top:insetY,left:insetX,bottom:insetY,right:insetX)
// collectionView.dataSource = self
// collectionView.delegate = self
}
}
// MARK UICollectionViewDataSource
extension ProductsViewController:
UICollectionViewDataSource
{
func numberOfSections(in collectionView: UICollectionView) ->
Int {
return 1
}
func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int) -> Int {
return products.count
}
func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier: "ProductsCollectionViewCell",for:indexPath) as!
ProductsCollectionViewCell
let product = products[indexPath.item]
cell.product = product
return cell
}
}
extension ProductsViewController: UIScrollViewDelegate, UICollectionViewDelegate
{
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let layout = self.collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
let cellWidthincludingSpacing = layout.itemSize.width+layout.minimumLineSpacing
var offset = targetContentOffset.pointee
let index = (offset.x + scrollView.contentInset.left) / cellWidthincludingSpacing
let roundedIndex = round(index)
offset = CGPoint(x: roundedIndex*cellWidthincludingSpacing-scrollView.contentInset.left, y: scrollView.contentInset.top)
targetContentOffset.pointee = offset
}
}

1, add didSelectItemAt to your collectionView, this will handle the click on the cell.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "showProducts", sender: categories[indexPath.item])
}
2, In your CategoriesViewController start type "prepare" and you will see the prepare for segue method.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showProducts" {
let destination = segue.destination as! ProductsViewController
destination.titleOfCategory = sender as? String
}
}

Related

Update Segmented Progress Bar When CollectionView Index is Changed Swift

Working Example Video
I am trying to create an onboarding view such as Instagram stories with a progress bar on top of the view.
So far I was able to animate to the second or third index in the given time. This animation also changes stack view progress bar. But when I try to scroll to the next or previous collection view index I can not show this action inside the progress bar.
I can read the current page index with page control but can not reflect this reading to progress bar.
class OnboardingViewController: UIViewController, SegmentedProgressBarDelegate {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var stackView: UIStackView!
private var spb: SegmentedProgressBar!
var currentPage = 0
lazy var pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.numberOfPages = slides.count
return pageControl
}()
//Burası Presenter'dan gelecek.
var slides: [OnboardingSlide] = [
OnboardingSlide(title: "Delicious Dishes", image: #imageLiteral(resourceName: "1")),
OnboardingSlide(title: "World-Class Chefs", image: #imageLiteral(resourceName: "2")),
OnboardingSlide(title: "Instant World-Wide Delivery", image: #imageLiteral(resourceName: "3"))
]
override func viewDidLoad() {
super.viewDidLoad()
spb = SegmentedProgressBar(numberOfSegments: slides.count, duration: 3)
spb.frame = CGRect(x: 15, y: 56, width: collectionView.frame.width - 30, height: 4)
spb.delegate = self
spb.topColor = UIColor.white
spb.bottomColor = UIColor.white.withAlphaComponent(0.25)
spb.padding = 2
collectionView.delegate = self
collectionView.dataSource = self
stackView.addArrangedSubview(spb)
spb.startAnimation()
collectionView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tappedView)))
}
func segmentedProgressBarChangedIndex(index: Int) {
updateView(index: index)
}
override var prefersStatusBarHidden: Bool {
return true
}
func segmentedProgressBarFinished() {
print("Finished!")
}
#objc private func tappedView() {
spb.isPaused = !spb.isPaused
}
private func updateView(index: Int) {
print("index: \(index)")
let indexPath = IndexPath(row: index, section: 0)
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
#IBAction func loginButtonTapped(_ sender: UIButton) {
}
#IBAction func signupButtonTapped(_ sender: UIButton) {
}
}
extension OnboardingViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return slides.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: OnboardingCollectionViewCell.identifier, for: indexPath) as! OnboardingCollectionViewCell
cell.setup(slides[indexPath.row])
return cell
}
}
extension OnboardingViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let width = scrollView.frame.width
currentPage = Int(scrollView.contentOffset.x / width)
pageControl.currentPage = currentPage
updateView(index: currentPage)
print("current page: \(currentPage)")
}
}
I have used
https://github.com/D-32/SegmentedProgressBar
as the segmentedProgressBar
class OnboardingCollectionViewCell: UICollectionViewCell {
static let identifier = String(describing: OnboardingCollectionViewCell.self)
#IBOutlet weak var slideImageView: UIImageView!
#IBOutlet weak var slideTitleLabel: UILabel!
func setup(_ slide: OnboardingSlide) {
slideImageView.image = slide.image
slideTitleLabel.text = slide.title
}
}
struct OnboardingSlide {
let title: String
let image: UIImage
}
extension OnboardingViewController: UICollectionViewDelegateFlowLayout {
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
if scrollView.panGestureRecognizer .translation(in: view).x > 0 {
spb.rewind()
} else {
spb.skip()
}
}
}
with this, I was able to understand if collection view was scrolled left or right. Thus calling related functions inside the spb does the trick.

My pictures wont show when I build and run uicollection view

import UIKit
class HomePageCell: UICollectionViewCell {
#IBOutlet private weak var containerView: UIView!
#IBOutlet weak var photos: UIImageView!
#IBOutlet weak var songname: UILabel!
#IBOutlet weak var songmaker: UILabel!
//#IBOutlet weak var pfppic: UIImageView!
#IBOutlet weak var viewcount: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
containerView.layer.cornerRadius = 6
containerView.layer.masksToBounds = true
}
var photo: Photo? {
didSet {
if let photo = photo {
photos.image = photo.image
songname.text = photo.name
songmaker.text = photo.makername
viewcount.text = photo.likecount
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.photos.image = nil
}
}
import UIKit
protocol PinterestLayoutDelegate: AnyObject {
func collectionView(
_ collectionView: UICollectionView,
heightForPhotoAtIndexPath indexPath: IndexPath) -> CGFloat
}
class PinterestLayout: UICollectionViewLayout {
// 1
weak var delegate: PinterestLayoutDelegate?
// 2
private let numberOfColumns = 2
private let cellPadding: CGFloat = 6
// 3
private var cache: [UICollectionViewLayoutAttributes] = []
// 4
private var contentHeight: CGFloat = 0
private var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
// 5
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// 1
guard
cache.isEmpty,
let collectionView = collectionView
else {
return
}
// 2
let columnWidth = contentWidth / CGFloat(numberOfColumns)
var xOffset: [CGFloat] = []
for column in 0..<numberOfColumns {
xOffset.append(CGFloat(column) * columnWidth)
}
var column = 0
var yOffset: [CGFloat] = .init(repeating: 0, count: numberOfColumns)
// 3
for item in 0..<collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
// 4
let photoHeight = delegate?.collectionView(
collectionView,
heightForPhotoAtIndexPath: indexPath) ?? 180
let height = cellPadding * 2 + photoHeight
let frame = CGRect(x: xOffset[column],
y: yOffset[column],
width: columnWidth,
height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// 5
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
// 6
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (numberOfColumns - 1) ? (column + 1) : 0
}
}
override func layoutAttributesForElements(in rect: CGRect)
-> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath)
-> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
extension HomePageViewController: PinterestLayoutDelegate {
func collectionView(
_ collectionView: UICollectionView,
heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat {
return photos[indexPath.item].image.size.height
}
}
import UIKit
struct Photo {
var image: UIImage
var name: String
var makername: String
var likecount: String
init(image: UIImage, name: String, makername: String, likecount: String) {
self.image = image
self.name = name
self.makername = makername
self.likecount = likecount
}
init?(dictionary: [String: String]) {
guard
let name = dictionary["Name"],
let makername = dictionary["Makername"],
let photo = dictionary["Photo"],
let likecount = dictionary["Likecount"],
let image = UIImage(named: photo)
else {
return nil
}
self.init(image: image, name: name, makername: makername, likecount: likecount)
}
static func allPhotos() -> [Photo] {
var photos: [Photo] = []
guard
let URL = Bundle.main.url(forResource: "Photos", withExtension: "plist"),
let photosFromPlist = NSArray(contentsOf: URL) as? [[String:String]]
else {
return photos
}
for dictionary in photosFromPlist {
if let photo = Photo(dictionary: dictionary) {
photos.append(photo)
}
}
return photos
}
}
import UIKit
final class HomePageViewController: UICollectionViewController {
// MARK: - Properties
var photos = Photo.allPhotos()
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
if let layout = collectionView?.collectionViewLayout as? PinterestLayout {
layout.delegate = self
}
self.collectionView.backgroundColor = .white
self.collectionView.dataSource = self
self.collectionView.delegate = self
let iconImage = UIImage(named: "name")
let imageView = UIImageView(image: iconImage)
self.navigationItem.titleView = imageView
}
private let sectionInsets2 = UIEdgeInsets(
top: 50.0,
left: 20.0,
bottom: 50.0,
right: 20.0)
private let itemsPerRow: CGFloat = 1
private let itemsPerRow1: CGFloat = 1
private let itemsPerRow2: CGFloat = 2
// MARK: - Private
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 1
} else if section == 1 {
return 1
} else {
return photos.count
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomePageHeaderCell", for: indexPath) as! HomePageHeaderCell
return cell
} else if indexPath.section == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomePageProfileBar", for: indexPath) as! HomePageProfileBar
return cell
} else if indexPath.section == 2 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomePageCell", for: indexPath) as! HomePageCell
cell.photo = photos[indexPath.item]
//cell.pfppic.image = UIImage(named: "pfppic")
//cell.pfppic.layer.cornerRadius = 15.0
return cell
}
return UICollectionViewCell()
}
}
extension HomePageViewController: UICollectionViewDelegateFlowLayout {
// 1
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// 2H
if indexPath.section == 0 {
let paddingSpace = 0 * (itemsPerRow + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: 50)
} else if indexPath.section == 1 {
let paddingSpace1 = 0 * (itemsPerRow1 + 1)
let availableWidth1 = view.frame.width - paddingSpace1
let widthPerItem1 = availableWidth1 / itemsPerRow1
return CGSize(width: widthPerItem1, height: 100)
} else {
let paddingSpace2 = sectionInsets2.left * (itemsPerRow2 + 1)
let availableWidth2 = view.frame.width - paddingSpace2
let widthPerItem2 = availableWidth2 / itemsPerRow2
return CGSize(width: widthPerItem2, height: widthPerItem2)
}
}
// 3
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets2
}
// 4
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return sectionInsets2.left
}
}
I have the array built and I don't know why any pictures won't show

How to reload Section in UICollection view

So i'm trying to implement a searchbar to search over different categories. After looking online a bit, I took a tutorial for UITableView and adapted it to my UICollectionView.
The searching works fine, as I am able to print the search results which go into the filteredCategories array.
Where I am having trouble is reloading the UICollectionView using the new filteredCategories array. I tried collectionView.reloadData() as well as collectionView.reloadSections(NSIndexSet(index: 1) as IndexSet) but with no success.
It seems as if the values are passed only once when the section is being built, and that I cannot change it.
Here is my code:
import UIKit
class ViewController: UIViewController {
var collectionView: UICollectionView!
let categories: [String] = ["Technology", "Science", "Entertainment", "Business", "Health", "Sports"]
var filteredCategories: [String] = ["none"]
var count = 0
var filteredCount = 6
var ready: [String] = ["none"]
var readyCount = 0
func filterIt() {
if isFiltering {
ready = filteredCategories
readyCount = filteredCategories.count
} else {
ready = categories
readyCount = categories.count
}
print("second", ready)
}
lazy var sections: [Section] = [
TitleSection(title: "NewsStand"),
// SearchSection(),
BasicGridSection(categories: ready, count: readyCount)
]
lazy var collectionViewLayout: UICollectionViewLayout = {
var sections = self.sections
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
return sections[sectionIndex].layoutSection()
}
return layout
}()
let searchController = UISearchController(searchResultsController: nil)
var isSearchBarEmpty: Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String) {
filteredCategories = categories.filter { (category: String) -> Bool in
print("filtered", filteredCategories)
return category.lowercased().contains(searchText.lowercased())
}
filterIt()
collectionView.reloadData()
setupCollectionView()
collectionView.reloadSections(NSIndexSet(index: 1) as IndexSet)
}
var isFiltering: Bool {
return searchController.isActive && !isSearchBarEmpty
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Compositional Layout"
self.view.backgroundColor = UIColor.white
self.title = "NewsStand"
setupCollectionView()
filterIt()
// 1
searchController.searchResultsUpdater = self
// 2
searchController.obscuresBackgroundDuringPresentation = false
// 3
searchController.searchBar.placeholder = "Search"
// 4
navigationItem.searchController = searchController
// 5
definesPresentationContext = true
}
func setupCollectionView() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: collectionViewLayout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.backgroundColor = UIColor.white
collectionView.register(UINib(nibName: "TitleCell", bundle: .main), forCellWithReuseIdentifier: TitleCell.identifier)
collectionView.register(UINib(nibName: "GridCell", bundle: .main), forCellWithReuseIdentifier: GridCell.identifier)
collectionView.register(UINib(nibName: "SearchCell", bundle: .main), forCellWithReuseIdentifier: SearchCell.identifier)
self.view.addSubview(collectionView)
collectionView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
collectionView.reloadData()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
collectionView.reloadData()
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
sections.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
sections[section].numberOfItems
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
sections[indexPath.section].configureCell(collectionView: collectionView, indexPath: indexPath)
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let articlesView = storyboard?.instantiateViewController(withIdentifier: "articleViewController") as? ArticleViewController else {
print("error")
return
}
// set the post id for the comments
articlesView.set(index: indexPath.item)
// present(articlesView, animated: true, completion: nil)
self.navigationController!.pushViewController(articlesView, animated: true)
}
}
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
filterContentForSearchText(searchBar.text!)
}
}
and Section:
struct BasicGridSection: Section {
// TODO: create a constant for the title of the header of type String
var categories: [String]
var numberOfItems: Int
// TODO: create an initializer to set the title
init(categories: [String], count: Int) {
self.categories = categories
self.numberOfItems = count
}
func layoutSection() -> NSCollectionLayoutSection? {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.1), heightDimension: .fractionalHeight(0.8))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(0.4))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 2)
let section = NSCollectionLayoutSection(group: group)
return section
}
func configureCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: GridCell.self), for: indexPath) as? GridCell
cell!.set(label:categories[indexPath.row])
return cell!
}
}
Thanks!

NSCollectionView as NSCollectionViewItem

Here is the window I want to achieve:
It is a NSViewController (RedViewController) with NSCollectionView (RedCollectionView) as view.
It item (BlueItem) has a NSCollectionView (BlueCollectionView) as view.
It item's item (GreenItem), has a NSImageView (GreenImageView) as view.
The RedCollectionView will scroll vertically, and its items will be selectable. However the BlueCollectionView is just for display, it won't scroll itself and won't be selectable.
Here is the code for the AppDelegate:
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
lazy var redViewController: RedViewController = {
let redViewController = RedViewController.init()
return redViewController
}()
lazy var window: NSWindow = {
let window = NSWindow.init(contentViewController: self.redViewController)
return window
}()
lazy var windowController: NSWindowController = {
let windowController = NSWindowController.init(window: self.window)
return windowController
}()
func applicationDidFinishLaunching(_ aNotification: Notification) {
self.windowController.showWindow(nil)
}
}
Here is the code for the RedViewController:
class RedViewController: NSViewController, NSCollectionViewDataSource, NSCollectionViewDelegate {
lazy var collectionViewLayout: NSCollectionViewFlowLayout = {
let collectionViewLayout = NSCollectionViewFlowLayout.init()
collectionViewLayout.itemSize = NSSize(width: 100.0, height: 100.0)
collectionViewLayout.minimumLineSpacing = 10.0
collectionViewLayout.minimumInteritemSpacing = 10.0
collectionViewLayout.sectionInset = NSEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
return collectionViewLayout
}()
class RedCollectionView: NSCollectionView { /* nothing here for now */ }
lazy var redCollectionView: RedCollectionView = {
let redCollectionView = RedCollectionView.init(frame: NSRect(x: 0.0, y: 0.0, width: 500.0, height: 500.0))
redCollectionView.collectionViewLayout = self.collectionViewLayout
redCollectionView.dataSource = self
redCollectionView.delegate = self
redCollectionView.isSelectable = true
redCollectionView.backgroundColors = [NSColor.red]
return redCollectionView
}()
override func loadView() {
let clipView = NSClipView.init()
clipView.documentView = self.redCollectionView
let scrollView = NSScrollView.init(frame: NSRect(x: 0.0, y: 0.0, width: 500.0, height: 500.0))
scrollView.contentView = clipView
self.view = scrollView
}
override func viewDidLoad() {
super.viewDidLoad()
self.redCollectionView.register(BlueItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("BlueItemID"))
}
func numberOfSections(in collectionView: NSCollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "BlueItemID"), for: indexPath)
guard let blueItem = item as? BlueItem else { return item }
return blueItem
}
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
indexPaths.forEach({ print($0) })
collectionView.deselectItems(at: indexPaths)
}
}
Here is the code for the BlueItem:
class BlueItem: NSCollectionViewItem, NSCollectionViewDataSource {
lazy var collectionViewLayout: NSCollectionViewFlowLayout = {
let collectionViewLayout = NSCollectionViewFlowLayout.init()
collectionViewLayout.itemSize = NSSize(width: 27.0, height: 27.0)
collectionViewLayout.minimumLineSpacing = 3.0
collectionViewLayout.minimumInteritemSpacing = 3.0
collectionViewLayout.sectionInset = NSEdgeInsetsMake(3.0, 3.0, 3.0, 3.0)
return collectionViewLayout
}()
class BlueCollectionView: NSCollectionView {
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
self.nextResponder?.mouseDown(with: event)
}
override func mouseUp(with event: NSEvent) {
super.mouseUp(with: event)
self.nextResponder?.mouseUp(with: event)
}
}
lazy var blueCollectionView: BlueCollectionView = {
let blueCollectionView = BlueCollectionView.init(frame: NSRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
blueCollectionView.collectionViewLayout = self.collectionViewLayout
blueCollectionView.dataSource = self
blueCollectionView.isSelectable = false
blueCollectionView.backgroundColors = [NSColor.blue]
return blueCollectionView
}()
override func loadView() {
self.view = self.blueCollectionView
}
override func viewDidLoad() {
super.viewDidLoad()
self.blueCollectionView.register(GreenItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("GreenItemID"))
}
func numberOfSections(in collectionView: NSCollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "GreenItemID"), for: indexPath)
guard let greenItem = item as? GreenItem else { return item }
return greenItem
}
}
And finally here is the code for the GreenItem:
class GreenItem: NSCollectionViewItem {
class GreenImageView: NSImageView { /* nothing here for now */ }
lazy var greenImageView: GreenImageView = {
let image: NSImage = NSImage.init(imageLiteralResourceName: "greenImage")
let greenImageView = GreenImageView.init(image: image)
return greenImageView
}()
override func loadView() {
self.view = self.greenImageView
}
}
When I launch the app, I get this result:
So I have 3 issues:
1. The BlueItem with it BlueCollectionView don't get the blue background expected.
2. The BlueCollectionView flow layout tries to display all the items on the first row. It looks like it doesn't know the collection view size.
3. When I try to select a BlueItem: if I click on an empty space inside the BlueItem, the RedCollectionView delegate is triggered as expected, but if I click on a green part inside the BlueItem, the RedCollectionView delegate isn't triggered.

accessing an array of images from 1 view controller to another, to add swipe gesture recognizer

i have a collection view which has an array of images. when i press on any of the images it will open that image in full screen in another class. i tried to add swipe gesture recognizer in the second view controller but i dont know how to access the array that is in the first view controller.
This is my first view controller that displays the images in collection view
class sowrController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
#IBOutlet weak var collectionView: UICollectionView!
var albums = [AlbumModel]()
let db : DBHelperMo2lfat = DBHelperMo2lfat()
var selectedIndex : Int = -1
var posts : Post!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
self.albums.removeAll()
self.albums.append(contentsOf: self.db.fetchAllImages())
self.collectionView.reloadData()
DataService.ds.REF_POSTS_SOWR.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
self.albums.removeAll()
for snap in snapshot {
print ("SNAP: \(snap)")
if let postDict = snap.value as? Dictionary<String, AnyObject>{
let album : AlbumModel = AlbumModel(id: postDict["id"] as! String, name: postDict["image_name"] as! String, path: postDict["image_path"] as! String, url: postDict["image_path"] as! String, localPath: "")
if let items = snap.children.allObjects as? [FIRDataSnapshot] {
for itemSnap in items {
if let albumSnap = itemSnap.value as? Dictionary<String, AnyObject> {
album.childAlbums.append(AlbumModel(id: albumSnap["id"] as! String, name: albumSnap["image_name"] as! String, path: albumSnap["image_path"] as! String, url: albumSnap["image_path"] as! String, localPath: ""))
}
}
}
self.albums.append(album)
}
}
self.collectionView.reloadData()
}
})
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.albums.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.BookCellReuseIdentifier, for: indexPath) as? collectionViewCellSowr {
let album = albums[indexPath.item]
cell.initWithAlbumModel(album: album)
return cell
}else {
return collectionViewCellSowr()
}
}
private struct Constants {
static let BookCellReuseIdentifier = "cell"
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexPath.row
self.performSegue(withIdentifier: "showAlbum", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "showAlbum"
{
let vc = segue.destination as! imageFullScreen
vc.images = self.albums[self.selectedIndex]
}
}
This is the second view controller that makes the images go in full screen
class imageFullScreen: UIViewController{
var images : AlbumModel?
let db : DBHelperMo2lfat = DBHelperMo2lfat()
#IBAction func pictureSwipe(_ sender: Any) {
}
#IBOutlet weak var caption: UILabel!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.caption.text = images?.imageName
let url = URL(string: (images?.imagePath)!)
self.imageView.sd_setImage(with: url, placeholderImage: nil, options: [.progressiveDownload,.retryFailed])
}
EDIT:
Ok, so here is a collection view controller that creates image view as a subview and responding to swipe gestures. Please make sure you have two images "Image" and "Image-1" in your assets folder.
//
// CollectionViewController.swift
// test
//
// Created by Yonatan Vainer on 05/08/2017.
// Copyright © 2017 Sensus Healthcare LLC. All rights reserved.
//
import UIKit
private let reuseIdentifier = "id"
class CollectionViewController: UICollectionViewController {
var imageView = UIImageView(frame: CGRect(x: 0, y: 100, width: 300, height: 300))
var index = 0;
let names = ["Image","Image-1"]
override func viewDidLoad() {
super.viewDidLoad()
//For left swipe
let left = UISwipeGestureRecognizer(target: self, action: #selector(self.goLeft(_:)))
left.direction = .left
imageView.addGestureRecognizer(left)
//For right swipe
let right = UISwipeGestureRecognizer(target: self, action: #selector(self.goRight(_:)))
right.direction = .right
imageView.addGestureRecognizer(right)
imageView.isUserInteractionEnabled = true
self.view.addSubview(imageView)
self.view.layoutSubviews()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// 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: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return names.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
let nail = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
nail.image = UIImage(named: names[indexPath.row])
cell.backgroundView = nail
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
imageView.image = UIImage(named: names[indexPath.row])
index = indexPath.row
}
func goLeft(_ gesture: UISwipeGestureRecognizer){
index += 1
if index<0{
index = 0
}
imageView.image = UIImage(named: names[index])
}
func goRight(_ gesture: UISwipeGestureRecognizer){
index -= 1
if index>1{
index = 1
}
imageView.image = UIImage(named: names[index])
}
// MARK: UICollectionViewDelegate
/*
// Uncomment this method to specify if the specified item should be highlighted during tracking
override func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return true
}
*/
/*
// Uncomment this method to specify if the specified item should be selected
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return true
}
*/
/*
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
return false
}
override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
return false
}
override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
}
*/
}
==================================================================
In storyboard, click on your collection view and embed navigation controller.
This will add a top bar with the back button.
Attached image.
I'm not sure I completely understand your question, because I don't understand what the array has to do with a gesture recognizer, but if you are just trying to access the array from the previous ViewController, this should work if you have a navigation controller :
let vcIndex = self.navigationController?.viewControllers.index(where: { (viewController) -> Bool in
if let _ = viewController as? sowrController {
return true
}
return false
})
let prevVC = self.navigationController?.viewControllers[vcIndex!] as! sowrController
let albums:[AlbumModel] = prevVC.albums