collectionView's didSelectItemAtIndexPath not working [Swift] - swift

I've been having this problem recently, and I couldn't seem to find a fix on any other similar questions. I have a collectionView which just display's colours (see below image) and I would like to store the cell's background colour in a variable to pass through an unwind segue, in order to set a background colour of another element. However, I can't seem to get this function to work as it has on previous projects and versions of this application and I was wondering if anyone knows what the problem might be ?
After pressing the 'add' button at the bottom, it unwinds and appends a new item to another collectionView, seen here:
I am able to get the name that the user has put in, however I have to use a default colour as I can't seem to get the didSelectItemAtIndexPath function to work.
Here's my code for the addViewController (first image)
import UIKit
import ChameleonFramework
class mondayViewController: UIViewController {
// collectionView
#IBOutlet var dayView: UICollectionView!
// data to display
var data = Day.mondayData()
// data from segues
var receivedName: String?
var receivedColour: UIColor?
override func viewDidLoad() {
super.viewDidLoad()
// set title
self.navigationController!.navigationBar.topItem!.title = "Monday"
// set datasource for collectionView
// datasource is self as the collectionViewDataSource methods are in the extension class below
dayView.dataSource = self
// make the navigationBar flat to flow onto the options tab
self.navigationController?.hidesNavigationBarHairline = true
}
#IBAction func unwindToMonday(segue: UIStoryboardSegue) {
if segue.identifier == "mondayUnwind" {
let subjectAdd = segue.sourceViewController as! mondayAddSubjectView
receivedName = subjectAdd.nameField.text
appendStuff()
}
}
func appendStuff() {
data.append(Day(name: receivedName, color: UIColor.flatRedColor()))
dayView.reloadData()
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
// data.append(Day(name: receivedName, color: receivedColour))
// dayView.reloadData()
// currently used for testing
#IBAction func mondayAddButton(sender: AnyObject) {}
}
// mandatory functions for UICollectionView to work
// this includes assigning the number of sections, how manycells are meant to be in the section(s) and what cell to use (mondayCell in this case)
extension mondayViewController: UICollectionViewDataSource {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! mondayCell
cell.data = self.data[indexPath.item]
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath)
}
// custom style the size of the collectionView. Changes the width to fill the screen and the height to 70
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let kWhateverHeightYouWant = 80
return CGSizeMake(collectionView.bounds.size.width, CGFloat(kWhateverHeightYouWant))
}
}
if anyone could let me know if I am doing something wrong, or if there is a solution to this issue, that would be awesome.
Cheers,
Rowan
UPDATE:
I have added the collectionViewDataSource and Delegate Methods to the main class (code below):
import UIKit
import ChameleonFramework
class mondayAddSubjectView: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var pageView: PageViewController = PageViewController()
#IBOutlet var selectColourLabel: UILabel!
// would be a public variable, however it is an internal class
internal var cellColour: UIColor!
// collectionView
#IBOutlet var colourPicker: UICollectionView!
// textfield for the user to input their subject name
#IBOutlet var nameField: UITextField!
// source of colours to use in the colour collectionView
private var data = collectionViewColors.createColor()
override func viewDidLoad() {
super.viewDidLoad()
self.colourPicker.delegate = self
colourPicker.dataSource = self
// gesture recognizer to determine when the user has tapped anywhere but the text field, which cancells the keyboard
let tap: UIGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mondayAddSubjectView.dismissKeyboard))
view.addGestureRecognizer(tap)
}
// dismiss keyboard when the user taps away from the text field (called above with #selector)
func dismissKeyboard() {
view.endEditing(true)
}
// MARK: - CollectionView Delegate and Data source methods
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath)
print("it works")
}
// choose how many sections are in the collectionView. as no content break is required, this will be 1
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
// choose how many items are in each section. as there is only 1 section, it will be the number of stored items
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
// set re-use identifier and the data that will go in each cell
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! colourCell
cell.data = self.data[indexPath.item]
return cell
}
}

Related

accessing struct massive in UITableViewCell

I've created an collection view with carName's there are 5 of them, after clicking for example Mercedes(one of the collection view's cell) I want to set label text its own values:carModel ( carName and carModel are both same struct properties ) in Table view which is already created by me, but I cant access carModel array
I tried for loop but it returns an error
for i in cars.carModel {
lbl.text = cars.carModel[i]
}
any solution will be appericated
// data source file
struct Cars {
let carName:String
let carModel:[String]
}
let cars:[Cars] = [
Cars(carName: "Mercedes", carModel: ["S Class","A Class", "B Class"]),
Cars(carName: "BMW", carModel: ["X5","X6","X7"]),
Cars(carName: "Ford", carModel: ["Fuison","Focus","Mustang"]),
Cars(carName: "Toyota", carModel: ["Camry", "Corolla"]),
Cars(carName: "Hyundai", carModel: ["Elantra"])
]
// table view cell file
class TableViewCell: UITableViewCell {
#IBOutlet var lbl: UILabel!
func configure(with cars:Cars){
lbl.text = cars.carName
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
// mainviewcontroller
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
//#IBOutlet weak var tableView
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cars.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell
cell?.setup(with: cars[indexPath.row])
return cell!
}
}
extension ViewController:UICollectionViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "TableViewController") as? TableViewController
self.navigationController?.pushViewController(vc!, animated: true)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.configure(with: cars[indexPath.row])
return cell
}
}
Here's my simulator:
img1
img2
what I want:expected result img
When you defined your UITableViewDataSource you set the number of rows in function method to return a constant (5). In cellForRowAt function you setup the cell with a Cars object.
I think you are mixing what data should be use for table view's datasource.
For collection view data source you have to:
Number of rows in section function should return: cars.count
Cell for item at function must configure CollectionViewCell with a Cars instance
I think all you are missing is saving which cell is being selected in collection view to use this to supply correct data to table view.
In collection view delegate when user taps on a cell you can either save the selected indexPath or Cars instance, this will be use by table view delegate. Let's call it selectedCar.
Then for table view data source you have to:
Number of rows in section function should return: selectedCar.carModel.count
Cell for item at function must configure TableViewCell with a CarModel instance (which is a String)
I created a small project in Github as a sample, is working as expected. Check it out. Hope this helps you. Sample video in Youtube.
Note: Setting datasource and delegate for tableView or collectionView in ViewController is not the best way to organize code but for sample code is ok.

UICollectionView Cells overlapping with each other when i use a button to increase cell height

I have a CollectionView setup and i have a button inside that increases the cell height by a fixed amount. However when it does this is overlapps with the cell below meaning the cell below doesnt move down to make space.
Ive been trying to figure this out for days and i cant seem to get it working.
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var colors = [UIColor.red,UIColor.blue]
#IBOutlet weak var col: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! cellCollectionViewCell
cell.frame.size.height = CGFloat(317 + cell.button.tag*45)
cell.backgroundColor = colors[indexPath.row]
return cell
}
#IBAction func increaseHeight(_ sender: Any) {
col.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
class cellCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var butt: UIButton!
#IBAction func incHeight(_ sender: UIButton) {
sender.tag = sender.tag + 1
}
}
Editing the frame of the cell directly is probably not the best approach. Instead override collectionView sizeForItemAtIndexPath, and specify the height change there. This should allow the collection view to re-layout correctly with the new cell height.
You can see other questions like this one for more details

How can I get an array of Strings i.e.. [String] to show up in the custom collection view cell using storyboard

In the Storyboard:
The datasource and delegate are connected from the LFCollectionView to the LFCollectionViewController. The reuseIdentifier is set to LFCell in the CollectionViewCell. The label inside the CollectionViewCell is named cellLabel and has an outlet to my custom LFCollectionViewCell. I want the outlet cellLabel to display the elements in my array of strings lFContainerLabel within each cell container in the collection. I am missing something, or going about this all wrong. I do not have a nib. I don't know how to use those. Is a nib a must in this scenario?
import UIKit
var lFContainerLabel: [String] = []
private let reuseIdentifier = "LFCell"
class LFCollectionView: UICollectionView { }
class LFNavigationController: UINavigationController { }
class LFCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var cellLabel: UILabel!
}
class LFCollectionViewController: UICollectionViewController,UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.register(LFCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in LFCollectionViewController: UICollectionView) -> Int {
return 1
}
override func collectionView(_ LFCollectionViewController: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return lFContainerLabel.count
}
override func collectionView(_ LFCollectionViewController: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let lFCell = LFCollectionViewController.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as UICollectionViewCell!
var myIndex: Int = 0
lFCell?.backgroundColor = UIColor.black
lFCell?.accessibilityElements = lFContainerLabel
myIndex = indexPath.item
lFCell?.cellLabel?.text = lFContainerLabel[myIndex]
return lFCell!
}
}

How to select multiple images from UICollectionView and transfer them to another View Controller?

The code is written in Swift. I'm building a social app where the user can make posts. I'm using Firebase as a backend (database, storage). So, I have a UICollectionView that gets all the photos from the photo library of the device and populate the collection view using a custom cell. In the same View controller, I have another custom cell that the user can use to take a photo and use it to make a post. To make it clearer:
If the user decides to take a photo, when they click on "Use photo" they need to be presented to a new view controller that should display the photo they just took along with other options (such as title, description and tags using UITextFields & UITextView).
If the user decides to select multiple photos from their own library, I have to somehow mark those photos/cells (i.e. using a button for a checkmark), add the selected photos to an array (with some limit, maybe 10 photos top). When they click "Next" button, the array needs to be sent to the New Post View Controller where all the images should be dynamically displayed maybe using a horizontal UICollectionView (?!) (with an option to remove an image if it was selected by accident) and again, as above, have the opportunity to add title, description, etc. Now, I cannot figure out how to do any of that.
I looked for a solution, but I'm kind of stuck on this for couple days now, so help is very much welcome!
Here's what I have in the Collection View controller (PS: I didn't include the part with the function that gets the images from the Photos)
import UIKit
import Photos
class PrePhotoPostVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var nextButton: UIBarButtonItem!
var photosLibraryArray = [UIImage]()
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
checkPhotoLibraryPermission()
setupCollectionViewDelegates()
}
#IBAction func cancelButtonPressed (_ sender: UIBarButtonItem) {
dismiss(animated: true, completion: nil)
}
#IBAction func nextButtonPressed (_ sender: UIBarButtonItem) {
nextButton.isEnabled = false
}
#IBAction func takeAphotoButtonPressed (_ sender: UIButton) {
// Camera Autorization
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { response in
if response {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.camera;
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
}
else {
print("Camera isn't available in similator")
}
}
else {
print("unautorized")
}
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return photosLibraryArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cellCamera = collectionView.dequeueReusableCell(withReuseIdentifier: cellPrePostCameraCell, for: indexPath)
return cellCamera
}
else {
let cellPhotoLibrary = collectionView.dequeueReusableCell(withReuseIdentifier: cellPrePostPhotoLibrary, for: indexPath) as! PrePhotoPostPhotoLIbraryCell
cellPhotoLibrary.awakeFromNib()
cellPhotoLibrary.photoLibraryImage.image = photosLibraryArray[indexPath.row]
return cellPhotoLibrary
}
}
}
A screenshot of what this UICollectionView looks like:
Here's my code from the Photo Library Cell:
import UIKit
class PrePhotoPostPhotoLIbraryCell: UICollectionViewCell {
// MARK: Outlets
#IBOutlet weak var photoLibraryImage: UIImageView!
// var selectedPhotos = [UIImageView]()
#IBAction func selectedButtonPressed(_ sender: UIButton) {
self.layer.borderWidth = 3.0
self.layer.borderColor = isSelected ? UIColor.blue.cgColor : UIColor.clear.cgColor
}
override func awakeFromNib() {
photoLibraryImage.clipsToBounds = true
photoLibraryImage.contentMode = .scaleAspectFill
photoLibraryImage.layer.borderColor = UIColor.clear.cgColor
photoLibraryImage.layer.borderWidth = 1
photoLibraryImage.layer.cornerRadius = 5
}
}
First of all declare an array of mutable type that will store the selected cells item in it.
var _selectedCells : NSMutableArray = []
then in your viewDidLoad function add below code.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//this will allow multiple selection on uicollectionviewcell
CollectionView.allowsMultipleSelection=true //CollectionView is your CollectionView outlet
}
Then, Implement delegate functions of collectionview for selecting and deselecting cells
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
//add the selected cell contents to _selectedCells arr when cell is selected
_selectedCells.add(indexPath)
collectionView.reloadItems(at: [indexPath])
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
//remove the selected cell contents from _selectedCells arr when cell is De-Selected
_selectedCells.remove(indexPath)
collectionView.reloadItems(at: [indexPath])
}
I'd suggest saving the NSIndexPath of the selected item in an array, and then using that for the basis of comparison in the delegate function cellForItemAt indexPath.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YOUR_CELL_Identifier", for: indexPath as IndexPath)
//add your tick mark image to the cell in your storyboard or xib file.
let tickImage = cell.viewWithTag(YOUR_IMAGE_TAG_HERE) as? UIImageView
//Show tickImage if the cell is selected and hide tickImage if cell is NotSelected/deSelected.or whatever action you want to perform in case of selection and deselection of cell.
if _selectedCells.contains(indexPath) {
cell.isSelected=true
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: UICollectionViewScrollPosition.top)
tickImage?.isHidden=false
}
else{
cell.isSelected=false
tickImage?.isHidden=true
}
return cell
}
In Order to send items to next controller, get all the items from selected indexpaths.

NSCollectionViewItem has nil outlets in itemForRepresentedObjectAtIndexPath

I have an NSCollectionView that displays a bunch of items that are called "ImageCollectionViewItem"s. I have an ImageCollectionViewItem.xib file that solely has an NSView that covers the item. I want to be able to dynamically change what is in that view, but when I try to reference it, it is nil. I already checked questions like: outlets in UIViewController nil in viewdidload , that is not my problem, I linked it and I have the filled circle
Here is the viewcontroller extension:
extension ViewController : NSCollectionViewDataSource {
func collectionView(collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return titles.count
}
func collectionView(collectionView: NSCollectionView, itemForRepresentedObjectAtIndexPath indexPath: NSIndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItemWithIdentifier("ImageCollectionViewItem", forIndexPath: indexPath) as! ImageCollectionViewItem
//This is what is nil:
let thisView = item.iconView
return item
}
}
And then here is the ImageViewCollectionItem code
class ImageCollectionViewItem: NSCollectionViewItem {
#IBOutlet weak var iconView: NSView!
var numItem: Int?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.whiteColor().CGColor
}
}
That is some odd stuff for sure in Cocoa world. But here is some flow which should help. Open your ImageCollectionViewItem.xib in Interface Builder and drag "Collection View Item" from Object Library, then select it and set your custom class name to ImageCollectionViewItem, then makeItemWithIdentifier should create non-nil item.
In my example I have Tile class:
class Tile: NSCollectionViewItem {
And here is how I create the tiles:
func collectionView(collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(collectionView: NSCollectionView,
itemForRepresentedObjectAtIndexPath indexPath: NSIndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItemWithIdentifier("Tile", forIndexPath: indexPath) as! Tile
return item
}
Here is the picture of IB:
If you follow these steps, your outlets will be connected upon creation. In my case I have 2 outlets:
class Tile: NSCollectionViewItem {
#IBOutlet weak var bigScrollView: ItemScrollView!
#IBOutlet weak var smallScrollView: ItemScrollView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidAppear() {
//
}
}
And they are both get connected as you can see on the picture: