UICollectionViewController can't reach cell - ios

I tried to change the text of my label inside an UICollectionViewCell. The ViewCell class is linked to the cell, the outlets are set and in the viewController its registered by
// Register cell classes
self.collectionView!.register(ImageCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
and in cellForItemAt...
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ImageCollectionViewCell
but I can't access the label. Same problem with an imageView. If I tried to set the text with:
cell.hund.text = "bla"
the label "hund" is known but it throws that the label is nil.
Thanks Tom
ImageCollectionVC
import UIKit
private let reuseIdentifier = "cell"
class ImageCollectionVC: UICollectionViewController {
var listOfImages = [Int: KKImage]()
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView!.register(ImageCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
listOfImages = xmlParseImageSource()
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return listOfImages.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ImageCollectionViewCell
let imgUrl = listOfImages[indexPath.row]?.imagePreviewUrl
do {
let bindData = try Data(contentsOf: imgUrl!)
let img = UIImage(data: bindData)
cell.hund.text = "bla"
//cell.img.image = img
//cell.img.image = UIImage(data: bindData)
} catch {
print(error.localizedDescription)
}
cell.backgroundColor = UIColor.blue
return cell
}
}
ImageCollectionViewCell
import UIKit
class ImageCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var img: UIImageView!
#IBOutlet weak var hund: UILabel!
}

Thomas in awake from nib just initialize your UIImageView and UILabel and add them to the cell, it should work :)
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code, you obviously need to set the frames/constraints as per your requirement
img = UIImageView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
hund = UILabel(frame: CGRect(x: 50, y: 0, width: 300, height: 40))
self.addSubview(img)
self.addSubview(hund)
}
In case you are using an xib don't register the class instead use the nib registration which should be:
self.collectionView.register(UINib.init(nibName: "ImageCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: reuseIdentifier )

Related

UITableview scrolling is not responding properly?

booktable.frame = CGRect(x: 0, y: booktopview.bounds.height, width: screenWidth, height: screenHeight-booktopview.bounds.height-tabbarView.bounds.height)
booktable.register(UITableViewCell.self, forCellReuseIdentifier: "mycell")
booktable.dataSource = self
booktable.delegate = self
booktable.separatorColor = UIColor.lightGray
booktable.backgroundColor = UIColor.clear
booktable.separatorStyle = .singleLine
bookview.addSubview(booktable)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(tableView == booktable)
{
let cell1 = booktable.dequeueReusableCell(withIdentifier: "mycell")
for object in (cell1?.contentView.subviews)!
{
object.removeFromSuperview();
}
let img :UIImageView = UIImageView()
let lbl : UILabel = UILabel()
img.frame = CGRect(x: 15, y: 15, width: 80, height: 130)
img.image = imgarray[indexPath.row]
img.layer.borderWidth = 1.0
img.layer.borderColor = UIColor.lightGray.cgColor
cell1?.contentView.addSubview(img)
imgheight = img.bounds.height
lbl.frame = CGRect(x: img.bounds.width + 40, y: (imgheight+40-80)/2, width: booktable.bounds.width-img.bounds.width + 40 - 100, height: 80)
lbl.text = imgname[indexPath.row]
lbl.numberOfLines = 0
lbl.textAlignment = .left
lbl.font = UIFont(name: "Arial", size: 23)
lbl.textColor = UIColor.black
cell1?.selectionStyle = .none
cell1?.contentView.addSubview(lbl)
return cell1!
}
The code shown above is for book table, which sometimes scrolls like normal and sometimes not scrolling at all. I am doing all the code programatically. I have tested this on both simulators and devices but still the problem exists. Any help is appreciated...
Create Custom UITableViewCell, let's say it is ListTableCell
class ListTableCell: UITableViewCell {
#IBOutlet weak var lblTemp: UILabel!
#IBOutlet weak var imgTemp: UIImage!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I've created UITableViewCell with xib like this and bind IBOutlets
Let's say we have struct Model and array like this
struct Model {
let image : UIImage
let name: String
}
for i in 0...10 {
let model = Model(image: #imageLiteral(resourceName: "Cat03"), name: "Temp \(i)")
array.append(model)
}
Now on ViewController viewDidLoad() method,
tableView.register(UINib(nibName: "ListTableCell", bundle: nil), forCellReuseIdentifier: "ListTableCell")
Implement UITableViewDataSource methods like this,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListTableCell") as! ListTableCell
let model = array[indexPath.row]
cell.lblTemp.text = model.name
cell.imgTemp.image = model.image
return cell
}
FYI
For different tableviews, you can create different custom cell the same way and cellForRowAt indexPath and numberOfRowsInSection method will change appropriately.
Let me know in case of any queries.
UPDATE
Follow this and this to create CustomTableCell programmatically

Found nil while setting value of a label inside Collection view cell

Case:
I'm using a collection view inside a UIView, I've connected it as an outlet called 'partsCollectionView'. I've created a cell with identifier 'cell' and a custom class 'SelectionCollectionViewCell' for that cell. Inside the cell, I've got a label called 'cellTitle'.
Error: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I'm facing the error while setting the value of that label inside 'cellForItemAt' Method.
Here are the concerned views:
Cell description,
and, collection View Description
The Cell Class is:
import UIKit
class SelectionCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var cellTitle: UILabel!
}
The class where I'll use the collection view is:
import UIKit
private let reuseIdentifier = "cell"
let array = ["small","Big","Medium","Very big","Toooo big String","small"]
class SelectionCollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var partsCollectionView: UICollectionView!
#IBOutlet weak var instructionsView: UITextView!
#IBOutlet weak var image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.partsCollectionView.register(SelectionCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = partsCollectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SelectionCollectionViewCell
cell.layer.cornerRadius = 10
cell.clipsToBounds = true
cell.cellTitle.text = array[indexPath.row]
cell.layer.borderColor = #colorLiteral(red: 0.07843137255, green: 0.6862745098, blue: 0.9529411765, alpha: 0.6819349315)
cell.layer.borderWidth = 2
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let name = array[indexPath.row]
let attributedName = NSAttributedString(string: name)
let width = attributedName.widthWithConstrainedHeight(height: 20.0)
return CGSize(width: width + 40, height: 30.0)
}
}
extension NSAttributedString {
func widthWithConstrainedHeight(height: CGFloat) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)
return boundingBox.width
}
}
Update: This is how it looks if I skip setting the title text. The title will come inside those rounded boxes.
Remove this line from viewDidLoad.
self.partsCollectionView.register(SelectionCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
When you register a cell programmatically it creates a new collection view cell object. It doesn't get the cell from storyboard. So cellTitle will be nil
OR
Programmatically initialize the label in custom collection view cell
class SelectionCollectionViewCell:UICollectionViewCell {
let cellTitle = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(cellTitle)
cellTitle.frame = CGRect(x: 10, y: 10, width: 100, height: 30)
}
}
Just minor mistake is there I think, In cellForItemAt indexPath Please update following line,
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SelectionCollectionViewCell
I just paste your code and got following output, nothing else,
If you still unable to produce desired output, just delete existing UILabel from SelectionCollectionViewCell and add it again.
FYI. No need to register cell in viewDidLoad() method

Cannot Click Custom UICollectionViewCell

I have a custom UICollectionView cell with an imageView and a label, as listed below:
import UIKit
class PollCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var pollQuestion: UILabel!
}
I want to make each populated cell clickable and then pass information from that clicked cell into a new ViewController. Below is the ViewController that populates the custom cell.
import UIKit
import FirebaseDatabase
import FirebaseDatabaseUI
import FirebaseStorageUI
private let reuseIdentifier = "PollCell"
class TrendingViewController: UICollectionViewController {
var ref: FIRDatabaseReference!
var dataSource: FUICollectionViewDataSource!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.dataSource = self.collectionView?.bind(to: self.ref.child("Polls")) { collectionView, indexPath, snap in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PollCell
/* populate cell */
cell.pollQuestion.text = snap.childSnapshot(forPath: "question").value as! String?
let urlPollImage = snap.childSnapshot(forPath: "image_URL").value as! String?
cell.imageView.sd_setImage(with: URL(string: urlPollImage!), placeholderImage: UIImage(named: "Fan_Polls_Logo.png"))
//Comment
return cell
}
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath)
}
}
I also have some code to "invert" the cells that populate in my inverted ViewController:
import Foundation
import UIKit
class InvertedStackLayout: UICollectionViewLayout {
let cellHeight: CGFloat = 100.00 // Your cell height here...
override func prepare() {
super.prepare()
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttrs = [UICollectionViewLayoutAttributes]()
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numberOfSectionItems = numberOfItemsInSection(section) {
for item in 0 ..< numberOfSectionItems {
let indexPath = IndexPath(item: item, section: section)
let layoutAttr = layoutAttributesForItem(at: indexPath)
if let layoutAttr = layoutAttr, layoutAttr.frame.intersects(rect) {
layoutAttrs.append(layoutAttr)
}
}
}
}
}
return layoutAttrs
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let contentSize = self.collectionViewContentSize
layoutAttr.frame = CGRect(
x: 0, y: contentSize.height - CGFloat(indexPath.item + 1) * cellHeight,
width: contentSize.width, height: cellHeight)
return layoutAttr
}
func numberOfItemsInSection(_ section: Int) -> Int? {
if let collectionView = self.collectionView,
let numSectionItems = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: section)
{
return numSectionItems
}
return 0
}
override var collectionViewContentSize: CGSize {
get {
var height: CGFloat = 0.0
var bounds = CGRect.zero
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numItems = numberOfItemsInSection(section) {
height += CGFloat(numItems) * cellHeight
}
}
bounds = collectionView.bounds
}
return CGSize(width: bounds.width, height: max(height, bounds.height))
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
if let oldBounds = self.collectionView?.bounds,
oldBounds.width != newBounds.width || oldBounds.height != newBounds.height
{
return true
}
return false
}
}
You must to implement this method of UICollectionViewDelegate
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
debugPrint("this works")
}
I hope this helps you
This Function used to select the row of your collection view
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
Then you use function call. Whatever you need, just Defined the inside function. And easily called the function during the selected the row (inside the didSelectItemAt indexPath)
Make sure you have enabled UserIntraction for both UILabel and UIImageView, because both have default value as false.
self.dataSource = self.collectionView?.bind(to: self.ref.child("Polls")) { collectionView, indexPath, snap in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PollCell
/* populate cell */
cell.pollQuestion.userInteractionEnabled = true;
cell.imageView.userInteractionEnabled = true;
cell.pollQuestion.text = snap.childSnapshot(forPath: "question").value as! String?
let urlPollImage = snap.childSnapshot(forPath: "image_URL").value as! String?
cell.imageView.sd_setImage(with: URL(string: urlPollImage!), placeholderImage: UIImage(named: "Fan_Polls_Logo.png"))
//Comment
return cell
}
Then you need to use of UICollectionViewDelegate method:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
}
Add a segue in your storyboard from the cell to the view controller you want to transition to. You can use the prepare for segue method, prepare(for:sender:), to pass your data. Using the segue (UIStoryboarySegue documentation) parameter, you can access the 'destinationViewController`.
Check out the View Controller for iOS Programming Guide: Using Segues for more information about using segues.

Passing data from custom UI cell to view controller

I am creating a pokedex app and the way I want it to work is basically there is a scroller at the top of the screen which allows you to select any pokemon and upon choosing the pokemon, underneath the scroller the entry for the pokemon will show up (bulbasaur will be there by default until a pokemon is selected because bulbasaur is the first pokemon with an ID of 1). To achieve this I have my view controller return two types of cells, the first being a "chooser cell" which is the scroller, and the second being a "description cell" which is the dex entry. I gave the view controller a data member called dex entry and return dex entry in the cellForItemAt function but the image of the cell is not changing (from bulbasaur to whichever pokemon I select). I print to the console what is the value of dex entry's pokemon every time a pokemon is selected so I am sure that the dex entry is being directly changed but I don't know why the image is not changing as well. Below are relevant parts of my code.
view controller (only part of it):
import UIKit
class PokeDexController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var dexEntry = DescriptionCell()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "PokeDex 386"
collectionView?.backgroundColor = UIColor(red: 52/255.0, green: 55/255.0, blue: 64/255.0, alpha: 1.0)
//collectionView?.backgroundColor = UIColor.white
collectionView?.register(chooserCell.self, forCellWithReuseIdentifier: cellID)
collectionView?.register(DescriptionCell.self, forCellWithReuseIdentifier: descID)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (indexPath.row == 0)
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! chooserCell
return cell
}
else{
let descCell = collectionView.dequeueReusableCell(withReuseIdentifier: descID, for: indexPath) as! DescriptionCell
dexEntry = descCell
return dexEntry
}
}
descriptionCell class:
import UIKit
class DescriptionCell: UICollectionViewCell
{
private var pokemon : Pokemon?
{
didSet
{
if let id = pokemon?._id
{
imageView.image = UIImage(named: String(id))
print("Pokemon with the id of " + String(id))
}
}
}
override init(frame: CGRect)
{
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setPokemon(poke: Pokemon)
{
self.pokemon = poke
}
func getPokemon() -> Pokemon
{
return pokemon!
}
let imageView: UIImageView =
{
let iv = UIImageView()
iv.image = UIImage(named: "1")
iv.contentMode = .scaleAspectFill
return iv
}()
func setupViews()
{
backgroundColor = UIColor(red: 52/255.0, green: 55/255.0, blue: 64/255.0, alpha: 1.0)
addSubview(imageView)
imageView.frame = (CGRect(x: frame.width/6, y: frame.height/30, width: frame.width/4, height: frame.height/4))
}
}
choosercell class (specifically the didSelectItemAt):
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
let poke = pokemon[indexPath.row]
print("Selected " + poke._name)
let vc = PokeDexController()
vc.dexEntry.setPokemon(poke: poke)
let name = vc.dexEntry.getPokemon()._name
print(name ?? "nothing there")
}
image of the app and the console output
any help is appreciated, thanks.
You need to change the dexEntry when you select a cell and reload the collection view cell.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
let poke = pokemon[indexPath.row]
print("Selected " + poke._name)
let cell = collectionView.cellForItem(at: IndexPath(row: 1, section: 0) as! DescriptionCell
cell.setPokemon(poke: poke)
collectionView.reloadItems(at: IndexPath(row: 1, section: 0))
}
Hope this helps.
I haven't solved my problem but I realize that the cell that I am returning in my viewController is independent of dexEntry so as far as I can cell, once that cell is set, it is set, so I now i will figure out how to reload things when a cell is selected so the cell that is returned has an image of a different pokemon.

Cannot load data from Parse in PFQueryCollectionViewController Swift iOS

I have problems loading Data from Parse into my PFQueryCollectionViewController. This is my WaterfallFeedCollectionViewController Class:
import UIKit
import Parse
class WaterfallFeedCollectionViewController: PFQueryCollectionViewController, CollectionViewWaterfallLayoutDelegate {
#IBOutlet var theCollectionView: UICollectionView!
//Random high Cells
lazy var cellSizes: [CGSize] = {
var _cellSizes = [CGSize]()
for _ in 0...20 {
let random = Int(arc4random_uniform((UInt32(100))))
_cellSizes.append(CGSize(width: 140, height: 150 + random))
}
return _cellSizes
}()
init!(style: CollectionViewWaterfallLayout, className: String!) {
super.init(collectionViewLayout: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Configure the PFQueryCollectionView
self.parseClassName = "foodEntry"
self.pullToRefreshEnabled = true
self.paginationEnabled = true
self.objectsPerPage = 15
}
override func queryForCollection() -> PFQuery {
var query = PFQuery(className: "foodEntry")
query.orderByAscending("createdAt")
return query
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let layout = CollectionViewWaterfallLayout()
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
layout.headerInset = UIEdgeInsetsMake(0, 0, 0, 0)
layout.headerHeight = 0
layout.footerHeight = 0
layout.minimumColumnSpacing = 10
layout.minimumInteritemSpacing = 10
theCollectionView.collectionViewLayout = layout
theCollectionView.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind: CollectionViewWaterfallElementKindSectionHeader, withReuseIdentifier: "Header")
theCollectionView.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind: CollectionViewWaterfallElementKindSectionFooter, withReuseIdentifier: "Footer")
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
//Zelle
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath, object: PFObject) -> PFCollectionViewCell {
var cell = theCollectionView.dequeueReusableCellWithReuseIdentifier("myWaterfallCell", forIndexPath: indexPath) as myWaterfallCollectionViewCell
cell.FoodNameLabel.text = object["FoodName"] as? String
var text = object["FoodName"] as? String
println("\(text) funktioniert")
return cell
}
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
var reusableView: UICollectionReusableView? = nil
if kind == CollectionViewWaterfallElementKindSectionHeader {
reusableView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Header", forIndexPath: indexPath) as? UICollectionReusableView
if let view = reusableView {
view.backgroundColor = UIColor.redColor()
}
}
else if kind == CollectionViewWaterfallElementKindSectionFooter {
reusableView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Footer", forIndexPath: indexPath) as? UICollectionReusableView
if let view = reusableView {
view.backgroundColor = UIColor.blueColor()
}
}
return reusableView!
}
// MARK: WaterfallLayoutDelegate
override func collectionView(theCollectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return cellSizes[indexPath.item]
}
}
It always returns nil. The documentation of Parse is really bad concerning the CollectionView. I found a sample project and tried to get it out with it and the way they load the data in the PFQueryTableView. The pull to refresh works so i guess its going the right way.
The code above actually works. There was just a Problem with the ParseUI framework which i now added via pods.

Resources