I have been trying to implement some custom cells in a table view I created in a view controller in the swift playgrounds iPad app. Please note that I have seen a lot of the other stack overflow posts dealing with similar issues but have not been able to find a solution that fixes my problem.
Here is my CustomCell Class:
class CustomCell: UITableViewCell {
let projectImage = UIImageView()
//let projectDivider = UIView()
let projectTitle = UILabel()
override func awakeFromNib() {
projectImage.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
//projectDivider.frame = CGRect(x: 100, y: 0, width: 20, height: 100)
projectTitle.frame = CGRect(x: 120, y: 0, width: 200, height: 100)
self.contentView.addSubview(projectImage)
//self.contentView.addSubview(projectDivider)
self.contentView.addSubview(projectTitle)
}
}
The following is my view controller code:
class AboutViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let tableView = UITableView()
let cellReuseIdentifier: String = "cell"
var projectTitles = ["Madhubani Coffee", "World Savvy", "Zipshare", "CrashThatCode", "Future Plans"]
var images: [UIImage] = [UIImage(named: "IMG_0061.JPG")!, UIImage(named: "IMG_0064.JPG")!, UIImage(named: "IMG_0062.JPG")!, UIImage(named: "IMG_0062.JPG")!, UIImage(named: "IMG_0062.JPG")!]
override func viewDidLoad() {
view.backgroundColor = UIColor.white
tableView.frame = CGRect(x: -82.5, y: 250, width: 600, height: 600)
tableView.delegate = self
tableView.dataSource = self
tableView.backgroundColor = UIColor.white
self.tableView.register(CustomCell.self as AnyClass, forCellReuseIdentifier: cellReuseIdentifier)
view.addSubview(tableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as! CustomCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: cellReuseIdentifier) as! CustomCell
}
cell.projectImage.image = images[indexPath.row]
cell.projectTitle.text = projectTitles[indexPath.row]
cell.clipsToBounds = true
return cell
}
}
I don't know what the problem is. It seems that the cells simply do not display. Any help would be appreciated!
Your problem is that you don't have a nib (or xib) file to do an awakeFromNib from.
Try doing this for your CustomCell instead:
class CustomCell: UITableViewCell {
let projectImage = UIImageView()
//let projectDivider = UIView()
var projectTitle = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
projectImage.frame = CGRect(x: 0, y: 0, width: 100, height: 30)
//projectDivider.frame = CGRect(x: 100, y: 0, width: 20, height: 100)
projectTitle.frame = CGRect(x: 10, y: 0, width: 200, height: 30)
projectTitle.font = UIFont.systemFont(ofSize: 20.0)
//self.contentView.addSubview(projectImage)
//self.contentView.addSubview(projectDivider)
self.contentView.addSubview(projectTitle)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Related
I'm trying to create a custom UIView (CardView) and display it in a UITableView
class CardView: UIView {
public var cardTitle = UILabel()
public var cardValue = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .secondarySystemBackground
self.addSubview(cardTitle)
self.addSubview(cardValue)
layer.cornerRadius = 10
cardTitle.frame = CGRect(x: 0, y: 10, width: self.width, height: 25)
cardValue.frame = CGRect(x: 0, y: cardTitle.bottom + 1, width: self.width, height: 25)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The TableView looks like this:
class StatsTableViewCell: UITableViewCell {
static let identifier = "StatsID"
private var cellTitleLabel: UILabel = {
let label = UILabel()
return label
}()
private var homeView: CardView = {
let card = CardView()
card.cardTitle.text = "Home"
return card
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview()
}
override func layoutSubviews() {
super.layoutSubviews()
cellTitleLabel.frame = CGRect(x: 10, y: 10, width: contentView.width - 20, height: 20)
homeView.frame = CGRect(x: 10, y: cellTitleLabel.bottom + 10, width: (contentView.width - 40) / 3, height: contentView.height - 25)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addSubview() {
contentView.addSubview(cellTitleLabel)
contentView.addSubview(homeView)
}
func configure(with viewModel: StatsTableviewViewModel) {
cellTitleLabel.text = viewModel.cellTitle
homeView.cardValue.text = viewModel.homeValue
}
}
On my cellForRowAt im tryin to configuring the cell like this:
extension WelcomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
viewmodels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: StatsTableViewCell.identifier, for: indexPath) as? StatsTableViewCell else {
fatalError()
}
print(viewmodels)
cell.configure(with: viewmodels[indexPath.row])
return cell
}
}
The problem is that in the StatsViewController it shows the homeView background and the cellTitleLabel but is not showing the cardTitle and cardValue
Screenshot of the StatsTableView
I'm using an extension to access easier to width and height. That's why you're not seeing .frame.size.height
I have a UITableView with 2 sections. In the first section I do not want the UIView I have created to appear on the left. It works fine when it initially loads but when it goes off screen and back on again it reappears.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! ATableCell;
cell.delegate = self;
if (indexPath.section == 1)
{
// let height = cell.bounds.size.height;
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.backgroundColor = UIColor.purple;
cell.addSubview(turnsView);
}
else
{
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.backgroundColor = UIColor.clear;
cell.addSubview(turnsView);
}
// Configure the cell...
cell.backgroundColor = UIColor.clear;
cell.textLabel?.text = "texting";
cell.detailTextLabel?.text = "testing";
}
I don't want the purple view appearing in the first section at any point.
When using
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
You should use Every "if" condition with its "else" clause as when you scroll on off it reuses the cell that cause it to reappear. So if you not write else condition and if condition become false it will use old cell data.
Always avoid adding subviews to cell in cellForRow. You need to understand that the cells are reused and when you add the subView in cellForRow, the subView is not removed each time. So if the cell appears for 10 times due to scrolling on and off, 10 subViews would be added to the reused cell.
In your case same thing is happening. I would suggest you to add the view in XIB/Prototype cell. Give it a reference and change its backgroundColor property in cellForRow.
Or if you are not using XIB or prototype cell, add subview in init method in your cell class.
var turnsView = UIView()
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 100))
self.contentView.addSubview(turnsView)
}
This should work for you:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! ATableCell;
cell.delegate = self;
//Remove subviews before adding again
if let purpleV = cell.viewWithTag(1) {
purpleV.removeFromSuperview()
}
if let purpleV = cell.viewWithTag(2) {
purpleV.removeFromSuperview()
}
if (indexPath.section == 1)
{
// let height = cell.bounds.size.height;
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.tag = 1
turnsView.backgroundColor = UIColor.purple;
cell.addSubview(turnsView);
}
else
{
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.tag = 2
turnsView.backgroundColor = UIColor.clear;
cell.addSubview(turnsView);
}
// Configure the cell...
cell.backgroundColor = UIColor.clear;
cell.textLabel?.text = "texting";
cell.detailTextLabel?.text = "testing";
}
Read the documentation for dequeuereusablecell method to clarify.
This will be good to do as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Config the cell...
if let turnsCell as? ATableCell {
turnsCell.turnsColor = (indexPath.section == 1 ? UIColor.purple : UIColor.clear)
}
cell.textLabel?.text = "texting"
cell.detailTextLabel?.text = "testing"
return cell
}
class ATableCell: UITableViewCell {
var turnsView = UIView()
var turnsColor: UIColor {
get {
return self.turnsView.backgroundColor
}
set {
self.turnsView.backgroundColor = newValue
}
}
override func awakeFromNib() {
super.awakeFromNib()
// let height = self.bounds.size.height;
let height = 100;
turnsView.frame = CGRect(x: 0, y: 0, width: 10, height: height)
self.contentView.addSubview(turnsView)
self.backgroundColor = UIColor.clear
}
}
I have a programmatic tableview nested in a caraousel, but when it displays, the programmatic cell is always set wider than the tableview its in. If I print the carousel item width, tableview width and cell width I get: 281, 281, 320(iphone5 screen width). I tried adding constraints to the cell but keep getting nil errors, I believe because the cell hasn't been made yet. So I'm not sure where to put the constraints so that I stop getting these errors, I only need the cell content view width to match the tableview width, but how do I get the tableview width in my custom cell which has been made programmatically? Heres my current code without any constraints:
TableView:
import UIKit
class SplitterCarouselItemTableView: UITableView {
var splitter: BillSplitter?
required init(frame: CGRect, style: UITableViewStyle, splitter: BillSplitter) {
super.init(frame: frame, style: style)
self.splitter = splitter
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
if Platform.isPhone {
let tableViewBackground = UIImageView(image: UIImage(data: splitter?.image as! Data, scale:1.0))
self.backgroundView = tableViewBackground
tableViewBackground.contentMode = .scaleAspectFit
tableViewBackground.frame = self.frame
}
self.backgroundColor = .clear
self.separatorStyle = .none
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(SplitterCarouselItemTableViewCell.classForCoder(), forCellReuseIdentifier: "splitterCarouselItemTableViewCell")
let cell: SplitterCarouselItemTableViewCell = tableView.dequeueReusableCell(withIdentifier: "splitterCarouselItemTableViewCell") as! SplitterCarouselItemTableViewCell
var item = ((allBillSplitters[tableView.tag].items)?.allObjects as! [Item])[indexPath.row]
if allBillSplitters[tableView.tag].isMainBillSplitter {
getMainBillSplitterItems(splitter: allBillSplitters[tableView.tag])
item = mainBillSplitterItems[indexPath.row]
}
let count = item.billSplitters?.count
if count! > 1 {
cell.name!.text = "\(item.name!)\nsplit \(count!) ways"
cell.price!.text = "£\(Double(item.price)/Double(count!))"
} else {
cell.name!.text = item.name!
cell.price!.text = "£\(item.price)"
}
return cell
}
Cell:
import UIKit
class SplitterCarouselItemTableViewCell: UITableViewCell {
var name: UILabel!
var price: UILabel!
var view: UIView!
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:)")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: "splitterCarouselItemTableViewCell")
self.backgroundColor = .clear
let width = Int(contentView.bounds.width)
let height = Int(contentView.bounds.height)
view = UIView(frame: CGRect(x: 0, y: 2, width: width, height: height - 4 ))
view.backgroundColor = UIColor.white.withAlphaComponent(0.5)
let viewHeight = Int(view.bounds.height)
let viewWidth = Int(view.bounds.width)
price = UILabel(frame: CGRect(x: viewWidth - 80, y: 0, width: 75, height: viewHeight))
price.font = UIFont.systemFont(ofSize: 15.0)
price.textAlignment = .right
name = UILabel(frame: CGRect(x: 5, y: 0, width: width, height: viewHeight))
name.font = UIFont.systemFont(ofSize: 15.0)
name.numberOfLines = 0
view.addSubview(name)
view.addSubview(price)
contentView.addSubview(view)
}
}
I understand the init shouldn't be filled with all that code and will be extracted later. Its just there until i resolve the width issue.
Thanks for any help.
The challenge is that the tableView.dequeueReusableCell(withIdentifier:) method indirectly calls init(frame:) on your UITableViewCell class. There are a couple of ways to solve this:
Override init(frame:) in SplitterCarouselItemTableViewCell and set your width there when you call super.init(frame:)
Modify your cell.frame in your func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) method
Using Autolayout is problematic with UITableView cells. You don't want to go messing with all the subviews there.
Try adding this
cell.frame = CGRect(x: CGFloat(Int(cell.frame.minX)),
y: CGFloat(Int(cell.frame.minY)),
width: tableView.frame.width,
height: cell.frame.height)
to your
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
This fixed the problem for me.
My UITableViewController and UITableViewCell were done programmatically
in viewDidLoad Grab your tableView Outlet or your tableView Instance if you made it programmatically, then access to rowHeight and give an Value to it : Like This code Below:
enter code here
private let MenuTableView:UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(FoodTableViewCell.self, forCellReuseIdentifier: FoodTableViewCell.identifier)
tableView.showsVerticalScrollIndicator = false
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(MenuTableView)
MenuTableView.rowHeight = 50
}
I making a tableView of different item. The item have a name, and a picture.
Here is what I get :
And I want :
1 - to delete the white space which appears at the beginning and the end of the table view
2 - for the separator to be all the width
Here is my code :
CustomTableView
// Controls
var interestsCategory:[InterestCategory];
// Load components when the view is loaded
override func viewDidLoad() {
super.viewDidLoad();
//Hide Nav bar
self.navigationController?.navigationBarHidden = true;
// Table View
self.tableView = UITableView(frame: CGRect(x: 0, y: 3.2 * self.view.frame.height / 13, width: self.view.frame.width ,height: 8.8 * self.view.frame.height / 13), style: .Grouped)
//self.tableView.style = UITableViewStyle.Grouped;
self.tableView.registerClass(ProximityInterestCategoryCell.self, forCellReuseIdentifier: "ProximityInterestCategoryCell");
self.tableView.tableFooterView = UIView(frame: CGRectZero)
self.tableView.tableHeaderView = UIView(frame: CGRectZero)
//self.view.addSubview(tableView);
}
init()
{
self.interestsCategory = [];
self.interestsCategory.append(InterestCategory(name:"FINANCE", image: UIImage(named: "business2.png")!));
self.interestsCategory.append(InterestCategory(name:"SPORTS", image: UIImage(named: "sports2.png")!));
self.interestsCategory.append(InterestCategory(name:"WEB",image: UIImage(named: "web2.png")!));
self.interestsCategory.append(InterestCategory(name:"TOURISM",image: UIImage(named: "tourism2.png")!));
self.interestsCategory.append(InterestCategory(name:"ENERGY",image: UIImage(named: "energy2.png")!));
self.interestsCategory.append(InterestCategory(name:"TECHNOLOGY",image: UIImage(named: "technology2.png")!));
self.interestsCategory.append(InterestCategory(name:"MUSIC",image: UIImage(named: "music2.png")!));
self.interestsCategory.append(InterestCategory(name:"CHEMICAL",image: UIImage(named: "chemical2.png")!));
self.interestsCategory.append(InterestCategory(name:"INSURANCE",image: UIImage(named: "insurance2.png")!));
self.interestsCategory.append(InterestCategory(name:"HEALTH",image: UIImage(named: "health2.png")!));
self.interestsCategory.append(InterestCategory(name:"FASHION",image: UIImage(named: "fashion2.png")!));
self.interestsCategory.append(InterestCategory(name:"REAL ESTATE",image: UIImage(named: "realestate2.png")!));
print(" number : \(self.interestsCategory.count)");
super.init(nibName: nil, bundle: nil);
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("numberOfRowsInSection");
return self.interestsCategory.count;
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("cellForRowAtIndexPath : \(indexPath.row)");
print(self.interestsCategory[indexPath.row].name);
let cellIdentifier = "ProximityInterestCategoryCell";
let cell = self.tableView.dequeueReusableCellWithIdentifier( cellIdentifier, forIndexPath: indexPath) as! ProximityInterestCategoryCell;
cell.nameLabel.text = self.interestsCategory[indexPath.row].name;
cell.nameLabel.sizeToFit();
cell.background.image = self.interestsCategory[indexPath.row].image;
cell.background.clipsToBounds = true;
return cell;
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
/*
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
print("heightForRowAtIndexPath");
return 120;
}*/
CustomCell
var background: UIImageView!
var nameLabel: UILabel!
var checkButton: UIButton!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor.clearColor();
selectionStyle = .None;
self.background = UIImageView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height:self.frame.height));
self.background.alpha = 1;
self.background.contentMode = UIViewContentMode.ScaleAspectFill;
self.background.clipsToBounds = true;
contentView.addSubview(background);
self.nameLabel = UILabel(frame: CGRect(x: 50, y: 0, width: self.frame.width-50, height:self.frame.height));
self.nameLabel.center.y = self.center.y;
self.nameLabel.textColor = UIColor.whiteColor();
self.nameLabel.backgroundColor = UIColor.clearColor();
contentView.addSubview(nameLabel);
self.checkButton = UIButton(frame: CGRect(x: 6 * self.frame.width/7.4, y: 0, width: 0.7 * self.frame.width/7.4, height: 0.8 * self.frame.height/13));
self.checkButton.center.y = self.center.y;
self.checkButton.setImage(UIImage(named: "RondNonCoche.png"), forState: UIControlState.Normal);
self.checkButton.setImage(UIImage(named: "RondCoche.png"), forState: UIControlState.Selected);
self.checkButton.clipsToBounds = true;
contentView.addSubview(checkButton);
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
}
override func layoutSubviews() {
super.layoutSubviews();
}
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
}
If someone could help me, that would be great :)
Issue1
Please set
self.tableView.delegate = self
self.tableView.dataSource = self
Issue 2 separator inset
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Remove seperator inset
if ([cell respondsToSelector:#selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsZero];
}
// Prevent the cell from inheriting the Table View's margin settings
if ([cell respondsToSelector:#selector(setPreservesSuperviewLayoutMargins:)]) {
[cell setPreservesSuperviewLayoutMargins:NO];
}
// Explictly set your cell's layout margins
if ([cell respondsToSelector:#selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
}
These space came from this line :
self.tableView = UITableView(frame: CGRect(x: 0, y: 3.2 * self.view.frame.height / 13, width: self.view.frame.width ,height: 8.8 * self.view.frame.height / 13), style: .Grouped)
It should be
self.tableView = UITableView(frame: CGRect(x: 0, y: 3.2 * self.view.frame.height / 13, width: self.view.frame.width ,height: 8.8 * self.view.frame.height / 13), style: .Plain)
style: .Plain doesn't add space where style: .Grouped does.
I can't get my custom UICollectionViewCell to be shown when run without put a breakpoint in func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell and debug step by step through it. After that my custom cells will be shown fine. Something wrong from xcode builder or my code?
p/s: I register my custom UICollectionViewCell by className like this: clvDishes.registerClass(PosItemsCollectionViewCell.classForCoder(), forCellWithReuseIdentifier: "DishesCollectionViewCell") and create cell's layout all in code(not using xib file)
Here is my code
class PosCollectionViewSource<T>: NSObject, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var items:[T] = []
var identifier:String!
override init() {
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(identifier + "CollectionViewCell", forIndexPath: indexPath)
switch(String(T)){
case "PosItemsInfo":
let itemsCell = cell as! PosItemsCollectionViewCell
let posItems = items as Any as! [PosItemsInfo]
itemsCell.updateCell(posItems[indexPath.item])
return itemsCell
default : break
}
return cell
}
}
class PosItemsCollectionViewCell: UICollectionViewCell{
private var _lblItemName:UILabel!
private var _lblItemPrice:UILabel!
private var _vOverlayInfo:UIView!
private var _imvPictureContainer:UIImageView!
private var _vSeparate:UIView!
private var _imvWarning:UIImageView!
private var _imvSeason:UIImageView!
private var _lblRemaining:UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let uiFont = UIFont(name: "HelveticaNeue-Thin", size: 17) as UIFont?
_lblItemName = UILabel()
_lblItemName.textColor = UIColor.whiteColor()
_lblItemName.font = uiFont
_lblItemPrice = UILabel()
_lblItemPrice.textColor = UIColor.whiteColor()
_lblItemPrice.font = uiFont
_vSeparate = UIView()
_vSeparate.backgroundColor = UIColor(hexString: "FFFFFF", a: 0.5)
_vOverlayInfo = UIView()
_vOverlayInfo.backgroundColor = UIColor(hexString: "#000000", a:0.21)
_imvPictureContainer = UIImageView()
_imvPictureContainer.image = UIImage(named: "pos_DefaultDishIcon")
_vOverlayInfo.addSubview(_lblItemName)
_vOverlayInfo.addSubview(_lblItemPrice)
_vOverlayInfo.addSubview(_vSeparate)
_lblRemaining = UILabel()
_lblRemaining.textColor = UIColor(hexString: "E86E2B")
_lblRemaining.SetBorder("E86E2B", radius: 0)
_lblRemaining.backgroundColor = UIColor.whiteColor()
_lblRemaining.font = UIFont(name: "HelveticaNeue-Medium", size: 20)
_lblRemaining.textAlignment = NSTextAlignment.Center
_imvSeason = UIImageView()
_imvSeason.image = UIImage(named: "pos_SeasonIcon")
_imvWarning = UIImageView()
contentView.addSubview(_imvPictureContainer)
contentView.addSubview(_vOverlayInfo)
contentView.backgroundColor = UIColor.whiteColor()
}
func updateCell(item:PosItemsInfo){
_lblItemName.text = item.itemName
_lblItemPrice.text = item.itemPrice?.toString("%.2f")
_lblRemaining.text = item.itemQtyRemaining?.toString()
if item.itemQtyStatus == PosItemQtyStatus.SoldOut{
contentView.addSubview(_imvWarning)
_imvWarning.image = UIImage(named: "pos_SoldOut")
}
else if item.itemQtyStatus == PosItemQtyStatus.LowInStock{
contentView.addSubview(_imvWarning)
contentView.addSubview(_lblRemaining)
_imvWarning.image = UIImage(named: "pos_LowStock")
_lblRemaining.text = item.itemQtyRemaining?.toString()
}
if item.itemIsSeason == true {
contentView.addSubview(_imvSeason)
}
}
override func layoutSubviews() {
_vOverlayInfo.frame = CGRect(x: 0, y: contentView.frame.height - 60, width: 205, height: 60)
_imvPictureContainer.frame = CGRect(x: 0, y: 0, width: 205, height: 160)
_lblItemName.frame = CGRect(x: 10, y: 0, width: contentView.frame.width - 20, height: _vOverlayInfo.frame.height/2 )
_lblItemPrice.frame = CGRect(x: 10, y: _lblItemName.frame.height, width: contentView.frame.width - 20, height: _vOverlayInfo.frame.height/2 )
_vSeparate.frame = CGRect(x: 8, y: _lblItemName.frame.height, width: contentView.frame.width - 16, height: 1 )
_lblRemaining.frame = CGRect(x: (contentView.frame.width - 50)/2, y: 40, width: 50, height: 50)
_imvWarning.frame = CGRect(x: 125, y: 0, width: 81, height: 74)
}
override func didMoveToSuperview() {
self.setNeedsLayout()
self.layoutIfNeeded()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
}
class PosDishesVCtrl: PosBaseController {
#IBOutlet weak var clvDishes: UICollectionView!
private var _clvDataSource:PosCollectionViewSource<PosItemsInfo>!
private var _items:[PosItemsInfo]!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: String(self.dynamicType), bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
modifiedOrUpdateUI()
renderDishes()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func modifiedOrUpdateUI(){
clvDishes.setCollectionViewLayout(LayoutGridType, animated: false)
clvDishes.registerClass(PosItemsCollectionViewCell.classForCoder(), forCellWithReuseIdentifier: "DishesCollectionViewCell")
clvDishes.backgroundColor = UIColor(hexString: "979797", a: 1)
clvDishes.showsHorizontalScrollIndicator = false
clvDishes.showsVerticalScrollIndicator = false
clvDishes.pagingEnabled = false
clvDishes.autoresizingMask = UIViewAutoresizing.FlexibleWidth
clvDishes.contentSize = CGSize(width: clvDishes.frame.width, height: clvDishes.frame.height)
clvDishes.scrollsToTop = false
}
func renderDishes(){
if(_clvDataSource == nil){
_clvDataSource = PosCollectionViewSource<PosItemsInfo>()
_clvDataSource.identifier = "Dishes"
_items = [PosItemsInfo]()
}
for var i = 0; i < 12; i++ {
let item = PosItemsInfo()
item.itemName = "Dish " + String(i)
item.itemPrice = Double(i)
item.itemQtyRemaining = 4
if(i%2 == 0){
item.itemIsSeason = true
item.itemQtyStatus = PosItemQtyStatus.LowInStock
}
else {
item.itemQtyStatus = PosItemQtyStatus.SoldOut
}
_items.append(item)
}
clvDishes.setCollectionViewLayout(LayoutGridType, animated: false)
_clvDataSource.items = _items
clvDishes.dataSource = _clvDataSource
clvDishes.delegate = PosCollectionViewDelegate(ctr: self)
clvDishes.reloadData()
clvDishes.collectionViewLayout.invalidateLayout()
}
}
Any idea would be appreciate!
It looks like your DataSource method:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int)
-> Int
{
return items.count
}
is always returning zero. Your row indices correspond to the number of items (cells) you'll return here.
EDIT:
Looking at the code you've provided, it isn't apparent that you ever add any items to the PosCollectionViewSource.items array, so I expect items.count to return zero. If it does not, then you must be adding items to the array somewhere else (in source code you haven't posted).
Looking at your code, I notice that the way you're setting up your custom cell class and using it is a bit complicated (possibly wrong) and could be better.
Assuming that you are adding elements to the items array somewhere, let's try a different approach:
Here's a pattern I use for cell types with reuseIdentifiers (UITableViewCell, UICollectionViewCell, etc.):
class CustomCell: UICollectionViewCell {
// This will do the right thing for CustomCell and its subclasses
class var reuseIdentifier: String { return "\(self)" }
}
Now, when you register the custom class, do it like this:
collectionView.registerClass(CustomCell.self, CustomCell.reuseIdentifier)
and fetch one like this:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(CustomCell.reuseIdentifier, forIndexPath: indexPath)
as! CustomCell
// setup the cell as necessary:
cell.contentView.backgroundColor = UIColor.yellowColor()
return cell
}
I had a very similar problem on iOS 14 (simulator & actual devices). Cells would only be displayed if I stopped running the app with a debug breakpoint before returning each cell.
A solution that worked for me was to call cell.setNeedsLayout() before returning the cells:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueCellOfType(MyCell.self, for: indexPath)
// Make the call below:
cell.setNeedsLayout()
return cell
}