Can you use the new UICollectionLayoutListConfiguration API in iOS 14 with collection views in storyboards?
I have a UICollectionViewController in a storyboard, which I configure with a custom list layout below:
var config = UICollectionLayoutListConfiguration(appearance: .plain)
let layout = UICollectionViewCompositionalLayout.list(using: config)
collectionView.setCollectionViewLayout(layout, animated: false)
collectionView.dataSource = dataSource
(In storyboards collection views can only be set with flow or custom layouts)
This uses a standard diffable data source:
return UICollectionViewDiffableDataSource(collectionView: collectionView) { (collectionView, indexPath, item) in
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "SomeCell",
for: indexPath
) as? SomeCell else {
fatalError("Couldn't dequeue cell \(reuseIdentifier)")
}
cell.setItem(item)
return cell
}
However I get some really weird behaviour, such as IBOutlets being nil when rotating the screen, despite everything working fine before rotation.
I've found no good way to debug what is going on here, the stack trace looks correct and the cell's class is initilised and this is running on the main thread.
I have replicated each and every step of your code and the end result is as expected for me.
Here's end result in portrait mode
Here's end result in landscape mode
What I intended to do was to show a list of numbers in the collection view, here are the necessary files for your reference:
The UICollectionViewController
import UIKit
private let reuseIdentifier = "SomeCell"
enum Section {
case main
}
class SampleCollectionViewController: UICollectionViewController {
var dataSource: UICollectionViewDiffableDataSource<Section, Int>!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.setCollectionViewLayout(getLayout(), animated: false)
configureDataSource()
}
func getLayout() -> UICollectionViewCompositionalLayout {
let config = UICollectionLayoutListConfiguration(appearance: .plain)
let layout = UICollectionViewCompositionalLayout.list(using: config)
return layout
}
func configureDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: self.collectionView, cellProvider: { (collectionView, indexPath, number) -> UICollectionViewListCell? in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? SomeCell else {
fatalError("Cannot create cell")
}
cell.label.text = number.description
return cell
})
var initialSnapshot = NSDiffableDataSourceSnapshot<Section, Int>()
initialSnapshot.appendSections([.main])
initialSnapshot.appendItems(Array(1...100), toSection: .main)
dataSource.apply(initialSnapshot, animatingDifferences: false)
}
}
The UICollectionViewListCell
import UIKit
class SomeCell: UICollectionViewListCell {
#IBOutlet weak var label: UILabel!
override func updateConstraints() {
super.updateConstraints()
label.textColor = .blue
}
}
The storyboard setup:
I did all this following standard steps nothing fancy here
Important Note: One potential place where I found your implementation different was that you have dequeued a cell withReuseIdentifier as "SomeCell" but the cell class you have shown throwing error is TextCell if you could provide more specifics on the same then it would be helpful to narrow down the error you are facing.
Here is simplified demo. Much of based on somehow replicated your code.
Tested with Xcode 12 / iOS 14
struct SomeItem: Hashable {
let id = UUID()
var value: String
}
class SomeCell: UICollectionViewCell {
#IBOutlet weak var label: UILabel!
var item: SomeItem!
func setItem(_ item: SomeItem) {
self.item = item
label.text = item.value
}
override func awakeFromNib() { // called when all outlets connected
super.awakeFromNib()
label.textColor = .blue
}
}
class ViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
let config = UICollectionLayoutListConfiguration(appearance: .plain)
let layout = UICollectionViewCompositionalLayout.list(using: config)
collectionView.setCollectionViewLayout(layout, animated: false)
collectionView.dataSource = dataSource
}
lazy var dataSource = {
UICollectionViewDiffableDataSource<Int, SomeItem>(collectionView: self.collectionView) { (collectionView, indexPath, item) in
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "SomeCell",
for: indexPath
) as? SomeCell else {
fatalError("Couldn't dequeue cell")
}
cell.setItem(item)
return cell
}
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
var snapshot = NSDiffableDataSourceSnapshot<Int, SomeItem>()
snapshot.appendSections([0])
snapshot.appendItems([SomeItem(value: "Jan"), SomeItem(value: "Fab"), SomeItem(value: "Mar")])
dataSource.apply(snapshot)
}
}
Nothing special in storyboard - only applied custom classes for view controller and cell (label is centred for simplicity) and made custom collection view layout:
Related
I use the MVVM architecture and after setting everything up there is no cell in the TableView and breakpoints in the cell file will not get hit.
In my code:
controller:
private lazy var tableView: BaseTableView = {
let tableView = BaseTableView()
tableView.register(MyCell.self, forCellReuseIdentifier: String(describing: MyCell.self))
tableView.rowHeight = 148
tableView.estimatedRowHeight = 148
self.view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.top.bottom.left.right.equalToSuperview()
}
return tableView
}()
let dataSource = RxTableViewSectionedReloadDataSource<MytSection>(configureCell: {(datasource, tableView, indexPath, model) -> UITableViewCell in
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MyCell.self), for: indexPath) as! MyCell
cell.model = model
return cell
})
viewModel.output
.items
.map { [MySection(header: "", items: $0)] }
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: bag)
The breakpoint in the cell file was not triggered. I think the cell was not called, but my delegate and datasource are both bound properly.
I'd like to know why.
notice that the below code works as expected. So the question is, what are you doing differently? Maybe that snp.makeConstraints isn't working the way you think it is. Maybe your viewModel.output.items isn't emitting any next events. Maybe your MySection is implemented incorrectly...
class MyViewController : UIViewController {
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: view.bounds)
tableView.register(MyCell.self, forCellReuseIdentifier: String(describing: MyCell.self))
tableView.rowHeight = 148
tableView.estimatedRowHeight = 148
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(tableView)
return tableView
}()
let dataSource = RxTableViewSectionedReloadDataSource<MySection>(configureCell: {(datasource, tableView, indexPath, model) -> UITableViewCell in
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MyCell.self), for: indexPath) as! MyCell
cell.model = model
return cell
})
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
Observable.just(["hello", "world"])
.map { [MySection(model: "", items: $0)] }
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: bag)
}
}
typealias MySection = SectionModel<String, String>
final class MyCell: UITableViewCell {
var model: String = "" {
didSet {
textLabel!.text = model
}
}
}
I am testing iOS15 and some new functionalities of UIKit. I've encountered some issues, not sure how to solve them. I did not change that code. This is just a piece of code that worked perfectly with the iOS 14, now after updating my target, it throws an error.
Xcode crashes the moment when my custom header for the UICollectionView of type UICollectionElementKindSectionHeader is being returned for the dataSource. Here is my code:
private func configureDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Follower>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, followers) -> UICollectionViewCell? in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FollowerCell.reuseId, for: indexPath) as! FollowerCell
cell.set(on: followers)
return cell
})
dataSource.supplementaryViewProvider = { (collectionView, kind, indexPath) in
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: FollowersCollectionHeaderView.reuseId,
for: indexPath) as! FollowersCollectionHeaderView
header.set(with: self.user)
return header
}
}
The log says:
the view returned from
-collectionView:viewForSupplementaryElementOfKind:atIndexPath: does not match the element kind it is being used for. When asked for a view
of element kind 'FollowersCollectionHeaderView' the data source
dequeued a view registered for the element kind
'UICollectionElementKindSectionHeader'.
I did cast UICollectionElementKindSectionHeader to FollowersCollectionHeaderView, therefore I am not sure what is the issue here.
I've watched WWDC21 what's new in UIKit but haven't seen any mentioning of any change for that particular code.
Any suggestions, what to fix in that code?
Here is the partial solution that I came up with. Apple recommends using object's ID as a reference for the collectionView cells.
enum Section { case main }
var dataSource: UICollectionViewDiffableDataSource<Section, Follower.ID>!
// MARK: - Collection View configurations
fileprivate lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: UIHelper.createCompositionalLayout())
collectionView.delegate = self
collectionView.backgroundColor = .systemBackground
collectionView.register(FollowersCollectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: FollowersCollectionHeaderView.reuseId)
view.addSubview(collectionView)
return collectionView
}()
fileprivate lazy var snapshot: NSDiffableDataSourceSnapshot<Section, Follower.ID> = {
var snapshot = NSDiffableDataSourceSnapshot<Section, Follower.ID>()
snapshot.appendSections([.main])
let itemIdentifiers = followers.map { $0.id }
snapshot.appendItems(itemIdentifiers, toSection: .main)
dataSource.apply(snapshot, animatingDifferences: true)
return snapshot
}()
fileprivate func updateData(with followers: [Follower]) {
snapshot = NSDiffableDataSourceSnapshot<Section, Follower.ID>()
snapshot.appendSections([.main])
let itemIdentifiers = followers.map { $0.id }
snapshot.appendItems(itemIdentifiers, toSection: .main)
dataSource.apply(snapshot, animatingDifferences: true)
}
fileprivate func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<FollowerCell, Follower.ID> { [weak self]
cell, indexPath, followerID in
guard let self = self else { return }
let followerArray = self.followers.filter { $0.id == followerID }
if let follower = followerArray.first {
cell.set(on: follower)
}
}
dataSource = UICollectionViewDiffableDataSource<Section, Follower.ID>(collectionView: collectionView) {
collectionView, indexPath, itemIdentifier in
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
for: indexPath,
item: itemIdentifier)
}
let headerRegistration = createSectionHeaderRegistration()
dataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in
return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath)
}
}
fileprivate func createSectionHeaderRegistration() -> UICollectionView.SupplementaryRegistration<FollowersCollectionHeaderView> {
return UICollectionView.SupplementaryRegistration<FollowersCollectionHeaderView>(
elementKind: FollowersCollectionHeaderView.reuseId) { [weak self] supplementaryView, elementKind, indexPath in
guard let self = self else { return }
supplementaryView.set(with: self.user)
}
}
I have a UICollectionView with cells of two types; some that load images from a URL, and others that load metadata from a URL and display it with an LPLinkView.
Below is my code for the cell that displays data via LPLinkView:
import UIKit
import LinkPresentation
class LinkItemLinkPreviewCell: UICollectionViewCell {
static let identifier = "kLinkPreviewCollectionViewCell"
var linkView: LPLinkView?
var urlMetadata: LPLinkMetadata?
#IBOutlet var containerView: UIView? = UIView()
#IBOutlet var titleLabel: UILabel? = UILabel()
#IBOutlet var dateLabel: UILabel? = UILabel()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.clipsToBounds = true
self.autoresizesSubviews = true
self.layer.cornerRadius = 20
}
override func prepareForReuse() {
super.prepareForReuse()
}
var linkItem: LinkPostDataObject? {
didSet {
titleLabel?.text = linkItem?.postTitle ?? ""
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd hh:mm"
dateLabel?.text = df.string(from: toDate((linkItem?.timeSent)!/1000)!)
let provider = LPMetadataProvider()
let url = URL(string: linkItem!.url)
if url != nil {
provider.startFetchingMetadata(for: URL(string: linkItem!.url)!) { (metadata, error) in
if let md = metadata {
DispatchQueue.main.async { [self] in
linkView = LPLinkView()
linkView?.metadata = md
linkView!.frame = containerView!.frame
containerView?.addSubview(linkView!)
linkView!.isUserInteractionEnabled = false
urlMetadata = metadata
}
}
}
}
}
}
}
Below is the code for the UICollectionViewCell that displays an image from a URL:
import UIKit
class LinkItemImageCell: UICollectionViewCell {
static let identifier = "kImageCollectionViewCell"
#IBOutlet var imageView: UIImageView? = UIImageView()
#IBOutlet var titleLabel: UILabel? = UILabel()
#IBOutlet var dateLabel: UILabel? = UILabel()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.clipsToBounds = true
self.autoresizesSubviews = true
self.layer.cornerRadius = 20
}
override func prepareForReuse() {
super.prepareForReuse()
}
var linkItem: LinkPostDataObject? {
didSet {
titleLabel?.text = linkItem?.postTitle ?? ""
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd hh:mm"
dateLabel?.text = df.string(from: toDate((linkItem?.timeSent)!/1000)!)
if linkItem?.url.isImage() == true {
let url = URL(string: linkItem!.url)
url!.getImageData(from: url!) { (data, response, error) in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() { [self] in
imageView?.image = UIImage(data: data)
}
}
}
}
}
}
And below is the code for my ViewController:
import UIKit
class LinkCollectionViewController : UIViewController, UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var collectionNameLabel: UILabel!
#IBOutlet weak var collectionSubTitleLabel: UILabel!
#IBOutlet weak var collectionCreatorLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem()
backButton.title = "Inbox"
self.navigationController?.navigationBar.topItem?.backBarButtonItem = backButton
collectionNameLabel.text = airlockStore.selectedChannel.channelName
collectionSubTitleLabel.text = airlockStore.selectedChannel.channelDescription
collectionCreatorLabel.text = airlockStore.selectedChannel.creator
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.alwaysBounceVertical = true
collectionView.register(UINib(nibName: "LinkImageItemCell", bundle: nil), forCellWithReuseIdentifier: "kImageCollectionViewCell")
collectionView.register(UINib(nibName: "LinkItemLinkPreviewCell", bundle: nil), forCellWithReuseIdentifier: "kLinkPreviewCollectionViewCell")
collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
collectionView.reloadData()
NotificationCenter.default.addObserver(self, selector: #selector(self.updateLinkCollection), name: Notification.Name("Link_Collection_Updated"), object: nil)
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
#objc func updateLinkCollection() {
collectionView.reloadData()
collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
}
// MARK: - UICollectionViewDataSource Delegate
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return airlockStore.selectedChannel.linkPosts.count
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
// MARK: - UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//Image
if airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item].url.isImage() == true {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LinkItemImageCell.identifier, for: indexPath) as? LinkItemImageCell
else { preconditionFailure("Failed to load collection view cell") }
cell.linkItem = airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item]
return cell
}
//URL
else {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LinkItemLinkPreviewCell.identifier, for: indexPath) as? LinkItemLinkPreviewCell
else { preconditionFailure("Failed to load collection view cell") }
cell.linkItem = airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item]
return cell
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "GoToLinkBlockViewController", sender: self)
airlockStore.selectedLinkBlock = airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item]
guard let cell: LinkItemLinkPreviewCell = collectionView.cellForItem(at: indexPath)! as? LinkItemLinkPreviewCell else {
return
}
airlockStore.selectedLinkBlock.urlMetadata = cell.urlMetadata
}
}
What I'm noticing is that the cells with the LPLinkView's are really slow to display data, and also that a LOT of refreshing happens when scrolling the the UICollectionView.
Any ideas or guidance on how I could improve performance here appreciated; really just trying to not reload the images/URL's of the cells as I scroll.
I don't have time to work through your code, so my answer is going to be general:
When your table view/collection view displays data that has to be fetched over the network, load that data into your data model, not into cells. When the network load completes and that entry in the model is updated, tell the corresponding cell to reload.
If you load data into your cells, as you scroll, the data you loaded before will be lost, and if you scroll back, you'll have to load it again. Ideally, save you loaded data to files in the caches directory so it will still be available if the user closes the view controller and then reopens it.
If you install downloaded data into your model then when you scroll away and scroll back the cell will render correctly as soon as it's displayed.
I have a UITableViewCell, and it contain a UICollectionView.
I put data for collection view in dataSource's tableview.
class HomeTableViewCell: UITableViewCell {
var disposeBag = DisposeBag()
#IBOutlet weak var collectionItems: UICollectionView!
var listItem: PublishSubject<HomeResultModel> = PublishSubject<HomeResultModel>()
let dataSource = RxCollectionViewSectionedReloadDataSource<SectionOfMenuData>(configureCell: {(_, _, _, _) in
fatalError()
})
override func awakeFromNib() {
super.awakeFromNib()
setup()
setupBinding()
}
override func prepareForReuse() {
disposeBag = DisposeBag()
}
func setup() {
collectionItems.register(UINib(nibName: MenuCollectionViewCell.identifier, bundle: nil), forCellWithReuseIdentifier: MenuCollectionViewCell.identifier)
collectionItems.register(UINib(nibName: RecipeCollectionViewCell.identifier, bundle: nil), forCellWithReuseIdentifier: RecipeCollectionViewCell.identifier)
collectionItems.rx.setDelegate(self).disposed(by: disposeBag)
collectionItems.rx.modelAndIndexSelected(HomeListModel.self).subscribe { (model, index) in
if let recipesID = model.recipesID {
tlog(tag: self.TAG, sLog: "recipesID : \(recipesID)")
self.recipeIdSelected.onNext("\(recipesID)")
}
}.disposed(by: disposeBag)
dataSource.configureCell = { (dataSource, collectionView, indexPath, item) -> UICollectionViewCell in
let cellType = dataSource.sectionModels.first?.type ?? .recipeCell
if cellType == .menuCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MenuCollectionViewCell.identifier, for: indexPath) as! MenuCollectionViewCell
cell.showInfo(item)
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RecipeCollectionViewCell.identifier, for: indexPath) as! RecipeCollectionViewCell
cell.showInfo(item.recipesName ?? "", imageLink: item.imageThumb ?? "")
return cell
}
}
}
func setupBinding() {
listItem.map { model -> [SectionOfMenuData] in
let modelID = try JSONEncoder().encode(model.id)
let modelResultID = String.init(data: modelID, encoding: .utf8)!
return [SectionOfMenuData(type: modelResultID == "0" ? .menuCell : .recipeCell, id: modelResultID, items: model.list)]
}.bind(to: collectionItems.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
}
I pass data for tableviewcell with code:
let dataSource = RxTableViewSectionedReloadDataSource<SectionOfHome> { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: HomeTableViewCell.identifier, for: indexPath) as! HomeTableViewCell
cell.listItem.onNext(item)
return cell
}
First show it ok, but when scroll tableview, I reset DisposeBag in:
func prepareForReuse{
disposeBag = DisposeBag()
}
and so tableview show blank.
How wrong was I in this regard?
Your subscription to listItems is getting disposed when the cell is reused and is never recreated. You should run the one-time tasks in awakeFromNib and move individual cell specific bindings to a function that you can call after resetting the disposeBag in prepareForReuse.
I have a view controller with a tableview. I want to register my tableview with a custom uitableviewcell that i created programmatically with a xib file. inside the custom cell i have a function that would change the outlets labels values and this is where i am getting a fatal found nil error. here is my code for the view controller.
class AllTasksViewController: UIViewController{
var tableView = UITableView()
func configureTableView(){
view.addSubview(tableView)
setTableViewDelegates()
tableView.rowHeight = 50
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
tableView.register(CustomCellNSB2.self, forCellReuseIdentifier: "CustomCellNSB2")
}
func setTableViewDelegates(){
tableView.delegate = self
tableView.dataSource = self
}
override func viewDidLoad(){
super.viewDidLoad()
configureTableView()
print("allTasksView loaded")
//add some positioning and size constraints here for allTasksView
}
}
extension AllTasksViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allTasks.count
}
// Cell Data and Configuration
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let task = allTasks[indexPath.row] // creating a new task from the already stored task depending on the indexpath.row if indexPath.row is 3 then the task is tasks[3]
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCellNSB2", for: indexPath) as! CustomCellNSB2 // setting the identifier ( we have already set in the storyboard, the class of our cells to be our custom cell)
cell.setTask(task: task) `// this is where my app is crashing`
if (task.value(forKey: "isComplete") as! Bool == true){
cell.labelsToWhite()
cell.backgroundColor = Colors.greencomplete
cell.selectionStyle = .none
} else {
cell.backgroundColor = .white //adjust for nightmode later
cell.labelsToBlack()
}
print("CellData Task :", task.value(forKey: "isComplete") as! Bool, task.value(forKey: "name") as! String)
// if !(task.value(forKey: "isComplete") as! Bool){
// cell.backgroundColor = task.isImportant ? .purple : .white //according to the task isImportant attribute we set the background color
// }
return cell
}
and here is my custom uitableview cell
import UIKit
import CoreData
class CustomCellNSB2: UITableViewCell {
#IBOutlet weak var taskLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
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
}
func setTask(task: NSManagedObject ){ debugger leads me here. this is where it's getting nil
taskLabel.text = task.value(forKey: "name") as? String
dateLabel.text = task.value(forKey: "date") as?
String
}
func labelsToWhite() {
taskLabel.textColor = .white
dateLabel.textColor = .white
}
func labelsToBlack() {
taskLabel.textColor = .black
dateLabel.textColor = .red
}
}
You have to register your NIB ... all you are doing is registering the class.
Change this:
tableView.register(CustomCellNSB2.self, forCellReuseIdentifier: "CustomCellNSB2")
to this:
tableView.register(UINib(nibName: "CustomCellNSB2", bundle: nil), forCellReuseIdentifier: "CustomCellNSB2")
If you have the xib/nib configured correctly, that may be all you need to do.