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
Related
I am migrating an Objective-C class to Swift. I use the collectionView: cellForItemAtIndexPath: to return a cell of a custom class.
How do I do what I want to do using Swift?
The language change makes no difference. Cocoa works the same way regardless of whether you talk to it with Swift or Objective-C. You register the cell class with collection view (probably in your viewDidLoad) — or the cell nib if the cell is to be loaded from a nib — and in your cellForItem you dequeue it, configure it, and return it.
I am trying to build a generic UITableViewController with a Realm Results<Object> as model.
These are my simplified classes:
Realm Object:
import RealmSwift
class Test: Object {
dynamic var name = ""
}
TableViewCell:
import UIKit
import RealmSwift
class RealmCell: UITableViewCell {
typealias Entity = Test // from above
var object: Entity? {
didSet {
if let object = object {
textLabel?.text = object.name
}
}
}
}
TableViewController:
import UIKit
import RealmSwift
class RealmTableViewController: UITableViewController {
typealias TableCell = RealmCell // From example above
var objects = try! Realm().objects(TableCell.Entity.self) {
didSet { tableView.reloadData() }
}
// MARK: - UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
let cell =
tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableCell
cell.object = objects[indexPath.row]
return cell
}
}
I can't figure out a way to make the TableCell typealias #IBInspectable. I've been trying with NSClassFromString(_:) without success.
Hope someone can help.
If I understand you correctly, you basically want to be able to specify the entity name in Interface Builder, yes? I.e. you want to be able to basically select your custom class from the inspector?
If so then that is unfortunately not directly possible. #IBInspectable can only be used with specific types, as is documented here:
You can add the IBInspectable attribute to any property in a class declaration, class extension, or category of type: boolean, integer or floating point number, string, localized string, rectangle, point, size, color, range, and nil.
You can, however, specify a string property as IBInspectable (with a meaningful default) and in your initializers deduct the class from that. This will leave open the possibility of a mistake, i.e. typo, but it can still work.
See also this answer.
Edit after your comment:
In this case that won't be possible I'm afraid (at least as far as I know, could be there's some deep level hackery I don't know of, but that would probably ugly as hell).
The problem is that what is specified in IB can only be evaluated at runtime, yet the typealias is something defined at compile time.
I think what you basically want is simply a protocol for the functionality every cell class should have (and on which the generic view controller relies). You won't even need a typealias then since a protocol is a type in itself.
The concrete cell classes can then be chosen based on an IBInspectable-ed string, the protocol could even define a method for this.
Depending on your scenario's details you might even write a common superclass for all cells. One that adopts (part of) the protocol already (you could even leave out the protocol in this case but I'd recommend using one for readability).
This obviously assumes you have all needed functionality for the view controller defined for your generic cells, but that's a problem you face in any case (even if you could use a typealias defined during runtime).
2nd edit after I looked at your sample code:
Okay, I looked at it again and can hopefully explain this a bit better now. Unfortunately I can't just add to your repository directly as it doesn't compile (I am missing the Realm framework/pod and even if I added that I'd probably win nothing cause I don't know what exactly you do with it).
As said in the comment, I'd say you don't need any further IBInspectable property to set a class. This should happen in your storyboard already, i.e. you should set the class value of a given prototype cell to one of the concrete cell classes you have. However, your generic RealmTableViewController doesn't need to know that class. You seem to want to give it knowledge about that if I understand you correctly, probably to have it prepare the cell correctly depending in its concrete characteristics. Don't do that (I'll get to what you want to do in viewDidLoad in a moment). Instead, define a protocol that all cells adopt and that the RealmTableViewController knows about. In your tableView(_:cellForRowAt:) method you use this protocol in the as! ... part when dequeueing the cell. The protocol should define a preparation method that each concrete class should implement and that is then called at this point by the RealmTableViewController.
This way your table view controller stays generic, it doesn't really know anything about the cells it displays, which is how things are intended.
Now to the problem that you (I think) face: You want to have the controller know which kind of prototype cell it uses so that it, too, can prepare specific things (which you could also outsource into a protocol, for example). This is problematic, because you're basically trying to build back a core aspect of a table view controller: It's capability to handle different kinds of cells at the same time. I conclude this from your other code, in viewDidLoad and the objects property, which ultimately seems to also depend on the class of the cell. That's clashing with the design of the table architecture itself, it's not just a syntactical problem. However, there's a solution: Another protocol, plus a "companion" class to the concrete cell classes.
For each cell class you have, define corresponding class that deals with the Realm stuff your view controller(s) need to do. Maybe for some cells you have the same object, then write them accordingly (the new class should have a property that defines which cell it corresponds to). Define a protocol that all of them adopt and that has at least two methods (maybe use better names, it's late here...): doControllerStuff, and getCellIdentifier.
Then your RealmTableViewController finally does get an IBInspectable. If that is set, the controller instantiates the companion object to the cell (which concrete class is used will obviously be depending on the value of the IBInspectable). Should the same companion handle multiple cells, the IBInspectable must also define which cell will be used, i.e. the companion must be configured correctly. You can use some kind of string convention, or even write a factory class that hides the concrete class from the view controller and just gives it back a correct object typed as the protocol.
Anyways, whatever you do in viewDidLoad based on the class so far, change that to the doControllerStuff method. You might even have some super- and subclasses if that can be common amongst cells/companions, that doesn't matter. Lastly, in your `tableView(_:cellForRowAt:) method, you don't use the identifier directly, but instead ask the companion object for the identifier.
There's a slight caveat here though: You must obviously ensure that the interface builder value for the cell's identifier is correctly set, i.e. it matches the IBInspectable that you set in the view controller instance. You can write a catch around this, though and use a default cell, of simply throw an exception.
This has become quite long and I hope it's understandable in spite of that. I don't have the time to make a nice graphic of this or something, so if you still have questions I suggest we take this to chat. :) Just ping me and we will (though I am a bit spare on time starting tomorrow).
I am currently using a custom collection view cell with separate nib file and implementation files. I am also handling a certain type of animation for scrolling. When there are many cells, some of them overlaps. So i am exploring an option of using various cell identifiers for different cells based on the cell index path. Is there any way to achieve this because the registering function require the user to define the cell identifier in advance?
you can use the registerClass method for different types of custom cells and different custom cell identifier. example in Swift:
searchResultsTableView.registerClass(HolidayItemTableViewCell.self, forCellReuseIdentifier: "holidayItemCell")
searchResultsTableView.registerClass(EmployeeSearchResultsTableViewCell.self, forCellReuseIdentifier: "employeeSearchResultCell")
searchResultsTableView.registerClass(OfficeSearchResultTableViewCell.self, forCellReuseIdentifier: "officeCell")
I've implemented an UICollectionView with a custom layout. I know how to present visual adornments for the entire collection view, but don't know how to present different image for every section. I need to get the instance of decoration view by indexPath. I found this on Apple Document:
Use this method to create a layout attributes object for a decoration view in the collection view. Decoration views are a type of supplementary view but do not present data that is managed by the collection view’s data source. Instead, they mostly present visual adornments for a section or for the entire collection view.
It is up to you to decide how to use the indexPath parameter to identify a given decoration view. Typically, you use the decorationViewKind parameter to identify the type of the decoration view and the indexPath information to distinguish between different instances of that view.
who can tell me how to do it in detail?
thanks a lot!
Answer my own question!
Thanks for this article!
First, subclass UICollectionViewLayoutAttributes and add any property you want.
Second, in the function layoutAttributesForDecorationViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes!, set the property.
Third, in the UICollectionReusableView class, override the function applyLayoutAttributes: to get the custom UICollectionViewLayoutAttributes instance that contains your property.
Done!
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.