Unable to access custom table cell variables - ios

I'm using Xcode 6.0.1, with Swift. I have a Table View which was working fine for a normal type of cell, but it's started displaying errors after I changed it to use a custom cell. I made a Table View Cell in the storyboard, made a Cocoa Touch Class file, MyCustomCell, which is a subclass of UITableViewCell, and set the custom cell in the storyboard to use this class, in the Identity inspector. I wired a text field I put into the custom cell in the storyboard to the MyCustomCell.swift file as a variable called someData.
The problem occurs here:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as MyCustomCell
cell.someData!.text = "testing"
return cell
}
Come runtime, the "testing" line gives me a fatal error: unexpectedly found nil while unwrapping an Optional value error, assuming that I manually registered the cell class in viewDidLoad, like this:
tableView.registerClass(MyCustomCell.self, forCellReuseIdentifier: "Cell")
If, instead, I never register the class, instead putting "Cell" in the storyboard as the Restoration ID for the custom cell, I get the same exact error nil error message, but on the "let cell =" line instead of the "testing" line.
The MyCustomCell class wires the someData variable and has init, awakeFromNib, and setSelected shells but nothing else.
I'm not really sure how to fix this. Maybe I need to create a nib for the custom cell or something? Any help would be appreciated.
(By the way, I'm using Core Data to store entities for each table cell, but that's not shown here, since I'm only concerned with getting custom cells working right now.)

You are using the Restoration Id (Identity Inspector) instead of the Indentifier (Attributes Inspector) in Interface Builder.

When UITableView is instantiated from storyboard calling
registerClass(_:forCellReuseIdentifier:) causes problems. You need to give reueseIdentifier in storyboard in attributes inspector.

Related

Delegate and Datasource for second Collection View

There is a second UIcollectionView in my app, which is having an imageview.
But every-time I get an error like this
Thread 1: "could not dequeue a view of kind: UICollectionElementKindCell with identifier SliderCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard"
Where SliderCell is the Id of first UICollectionview
I know that this is because the identifier is not registered / misspelled. and I tried registering. But the same error happens.
What I found is when I add the delegate and Datasource of the second CollectionView to the same View, this error happens.
is there any solution for this?
[Solved]
Before :
first I declared dequeueReusableCell method for both cells, set the cell values and put the return statement in an if-else condition.
After:
all three lines for first collectionView in if and declared other in else

Difference between self.collectionView and UICollectionViewCell.self

I know that self.collectionView for example refers to the property of the current instance. However, when i encounter this syntax UICollectionViewCell.self in:
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellID")
From apple doc:
func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String).
cellClass: The class of a cell that you want to use in the collection view.
I am confusing what does the syntax actually refers to?
You understand that "self.collectionView" refers to the collection view that you created and present on the screen. You (or interface builder) create that view. "self", in this case is the view controller which contains your collection view. That's half the story -- the other half is done by the system.
Your collection view gets filled with data, cell by cell. You need to tell your collection view what kind of cell to use (it can be a custom-designed cell, or a plain old UICollectionViewCell). The way you do that is to "register" your cell. This tells the collection view what kind of cell to create when it needs one.
One last point before I tie it all together: you can have different kinds of cells in your one collection view. Maybe some have images, some cells might just be plain text, others could have a combination. The way this made possible is by requiring you to register your cells with an associated ID, to make it easier for you to select a certain type of cell whenever a cell is needed.
The registration syntax accomplishes two things. For each type of cell you want to use (and very often it is only one), you register the kind of cell you want. That's where the "UICollectionViewCell.self" comes in. It could be "MyCell.self", etc. The .self here just means use this class as the template to create the cell. The second part of the registration is to attach the identifier to the template/class for easy reference.
self.collectionView - self is the view controller
UICollectionViewCell.self - self is the "instancetype" of the cell to be used. The system uses this information to construct a cell for you.
TypeName.self will return the type of the TypeName if that TypeName inherits from NSObject class, this is a method in NSObject class as Abhishek suggested
- (instancetype)self;
If you have made a subclass of say UICollectionViewCell
class CustomCell: UICollectionViewCell {
}
you would use it like this
collectionView?.register(CustomCell.self, forCellWithReuseIdentifier: "cellId")
This will return CustomCell (instanceType or AnyClass)
Hope it helps.
UICollectionViewCell.self refers to the type of Class you want to use for cell.
- (instancetype)self;
As you wrote
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellID")
This referring that you are about to use predefined collectionviewcell,
of-course if we define custom cell then we write it as
self.collectionView.register(UINib(nibName: "MyCollectionViewCell",bundle : nil), forCellWithReuseIdentifier: "MyCollectionViewCell")
when we type self.is referring that we are using current class variables
func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String).
This method tells the collection view that which Cell Class's objects it is going to reuse. This is how you can understand this method signature.
The register method needs two parameter one is a kind of AnyClass and another is of String. AnyClass means you are going to pass the class whose objects can be reused for collection view cell. You just need to mention the type of object (Class), not any particular object.
For more about types see Types

Subclassing UITableViewCell and connecting outlets

I'm working in Swift 3 and Xcode 8.
I have a number of table view cell prototypes where the cells are very similar, but there are enough differences where I want to make a subclass of UITableviewCell to hold all the similar outlets and the basic initialization, but then create a few classes which subclass off of the new class.
Question is, how do I get the outlets for these cells all connected to the outlets in the base class?
I can do this for the first cell, but then IB sees those outlets as connected, and won't let me connect them from the next subclassed cell.
I copied the first prototyped cell and pasted it in, then changed it's class to the second type. This still showed the outlets connected, and this seems to work, however, an action for a button in the second cell is not working.
What's the trick to getting all the different subclassed cells to have their outlets and actions connected to the base cell class?
If you have different classes for different cell then take objects of those classes and by using them you will be able to use your different cell's label,button etc
let cell1 = tableView.dequeueReusableCellWithIdentifier("Cell1",forIndexPath: indexPath) as! YourSubClass1
let cell2 = tableView.dequeueReusableCellWithIdentifier("Cell2",forIndexPath: indexPath) as! YourSubClass2
You need to remove the outlets of the second cell and connect them again to the subclass outlet properties after changing the second class type.
If you don't remove the outlets, it will still refer to the outlets of the first cell and that won't work.

Proper place to update UITableViewCell content [duplicate]

This question already has answers here:
iOS UITableView: what's the different between "cellForRowAtIndexPath" and "willDisplayCell: forRowAtIndexPath:"
(6 answers)
Closed 6 years ago.
I've always updated the UITableViewCell content in tableView:cellForRowAtIndexPath: like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.resultsTableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as UITableViewCell!
// update cell content
cell.backgroundColor = UIColor.clearColor()
cell.textLabel?.text = myTextString
return cell
}
The documentation also shows content being updated in tableView:cellForRowAtIndexPath:. However, today I read
The content of the cell has to be set right before it is displayed. We don’t care which cell object is being used. All we care about is that it has the right stuff to display when it is passed on to the renderer. So, the right place to set the cell’s content is tableView:willDisplayCell:forRowAtIndexPath:
The reason for this is mentioned in the same post. It talks about the
...problem where the custom cell appears to ‘randomly change content’ when the table is scrolled. The reason why this happens is because the content of the cell is set in the tableView:cellForRowAtIndexPath: method, which is not the right place. In this method, the system expects only to obtain the cell object. Once a cell is displayed, the system will not ask for the cell object until it loses it (i.e. when the cell object is destroyed) and cannot find a replacement to reuse. If, however, a replacement is found, it is reused, and you will end up seeing the contents of that reused cell in place of the original cell.
Is that correct? I've never noticed a problem before, but should I be updating my cell content in tableView:willDisplayCell:forRowAtIndexPath:? Have I been misled by the documentation?
The statement is wrong.
custom cell appears to ‘randomly change content’ when the table is scrolled
occurs only if
dequeueReusableCellWithIdentifier is (mis)used outside cellForRowAtIndexPath.
The entire content of the cell is not set reliably to a defined state inside cellForRowAtIndexPath.
The content of the cell is manipulated outside cellForRowAtIndexPath.
The developer is strongly discouraged from using any of the three cases.
So it's perfectly fine to both supply the cell and update the content in cellForRowAtIndexPath.
PS: I guess willDisplayCell:forRowAtIndexPath: is a legacy method coming from the cell based NSTableView in macOS where the data source delegate methods don't support to change the cell's appearance.

Strange error when adding items to prototype cells in storyboard-IB

I have quite a large project (~20 scenes). One of which is a TableViewController with a custom UITableViewController class. I have given the cell a reuse identifier, and added a label to it. When I try and Ctrl+Drag the label to the UITableViewController header file to create an outlet, I get the following error:
error: Illegal Configuration: Connection "tableInfoView" cannot have a
prototype object as its destination.
What is this? Am I overlooking something obvious? Or do I need to create a custom cell class and drag the outlet to that? If so, how do I then specify the data which is displayed uniquely for each cell from the UITableViewController?
In fact you can't just make an outlet from a dynamic cell prototype in the UITableView delegate view controller.
You'll have to subclass UITableViewCell and then attribute this class to your prototype.
Then you can Ctrl-Drag from the Label to the UITableViewCell subclass header file.
Finaly you can access to this outlet in the delegate code after having imported the UITableViewCell header file in it.
This is documented by Apple there at "The Technique for Dynamic Row Content" section.
or you could give the label a tag (e.g. 100) and use
myLabel = [myTableView viewForTag:100];
to get the label
I had the same error myself. Just to add one more potantial root cause for future readers:
In my case I copied a control (a Button in this case) from one prototype cell to the next and the action still referred to the neighbor cell. My table has several different prototype cells.
The fact, that it acutally was a proper subclass of UITableViewCell which was properly connected to the prototype cell made it difficult to actually see the mistake.
Tag the label and you can reach the label anywhere in the viewcontroller like with viewWithTag from the table view.
UILabel *destinationLabel = (UILabel *)[self.tableView viewWithTag:1];
destinationLabel.text = #"Label Destaination";
I faced the same problem but later it turned out that it was just a silly mistake.
I mistakenly dragged the label from Cell to my controller's #interface
This could be your problem too. just cross check once.
Set the right reuse identifier used in .m file in the Storyboard for the Prototype cell.I had the same situation and this helped me
After doing every thing right if problem still exist then just removed all outlets and rejoin them carefully and it worked very fine for me.

Resources