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
Related
I have done a UITableViewCell programmatically and it worked just fine with iOS 10. But after updating with iOS 11 and XCode 9, it behaves differently. The layout looks scrambled as below.
But if I tap on the cell then it rearranges and looks fine as below.
Here the code for UITableViewCell
import UIKit
import SnapKit
class AboutCell: UITableViewCell {
let roleLabel : UILabel = {
var tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 22)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
let nameLabel : UILabel = {
let tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 16)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
let webUrlLabel : UILabel = {
let tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 16)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
override func layoutSubviews() {
super.layoutSubviews()
roleLabel.frame = CGRect(x: 0, y: 10, width: self.contentView.bounds.size.width-20, height: 25)
nameLabel.frame = CGRect(x: 0, y: roleLabel.frame.origin.y+25, width: self.bounds.size.width-20, height: 25)
webUrlLabel.frame = CGRect(x: 0, y: nameLabel.frame.origin.y+25, width: self.bounds.size.width-20, height: 25)
}
func setupViews(){
contentView.addSubview(roleLabel)
contentView.addSubview(nameLabel)
contentView.addSubview(webUrlLabel)
}
func setValuesForCell(contributor : Contributors){
roleLabel.text = contributor.contributorRole
nameLabel.text = contributor.contributorName
webUrlLabel.text = contributor.contributorWeb
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
and the extension I wrote for TableView delegate and datasource
extension AboutController : UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contributorList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : AboutCell = tableView.dequeueReusableCell(withIdentifier: self.cellid, for: indexPath) as! AboutCell
cell.selectionStyle = .default
let contributor : Contributors = contributorList[indexPath.row]
cell.setValuesForCell(contributor: contributor)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
tableView.deselectRow(at: indexPath, animated: true)
}
}
and the ViewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100.0
tableView.rowHeight = 100
tableView.register(AboutCell.self, forCellReuseIdentifier: self.cellid)
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.top.right.bottom.left.equalToSuperview()
}
let mayu = Contributors(contibutorRole: "Developer", contributorName: "J Mayooresan", contributorWeb: "http://jaymayu.com")
let janie = Contributors(contibutorRole: "Voice Artist", contributorName: "M Jananie", contributorWeb: "http://jaymayu.com")
let arjun = Contributors(contibutorRole: "Aathichudi Content", contributorName: "Arjunkumar", contributorWeb: "http://laymansite.com")
let artist = Contributors(contibutorRole: "Auvaiyar Art", contributorName: "Alvin", contributorWeb: "https://www.fiverr.com/alvincadiz18")
contributorList = [mayu, arjun, janie, artist]
tableView.delegate = self
tableView.dataSource = self
self.tableView.reloadData()
}
Since you're laying out your tableView using autolayout, you also need to ensure translatesAutoresizingMaskIntoConstraints is set to false.
SnapKit should be setting the tableView's translatesAutoresizingMaskIntoConstraints to false for you already.
Since you're laying out the cells manually (using frames in layoutSubviews). Setting the cells subview's translatesAutoresizingMaskIntoConstraints to false is likely not needed.
See Apple Docs here for translatesAutoresizingMaskIntoConstraints
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")
}
}
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
}