Overriding the section row data in expand table view, when there are multiple sections of same type - swift

I have created a Collapsible table view using sections and which in-turn contains a single row.Initially tableview has 4 sections and My last section has ability of adding 3rd section multiple times when the user taps on the last.
Now am facing an issue when the user expands the section and enters the data and after that when he expands the dynamically added section the same data appearing in the newly added only also and it is vice-versa if the user modifies any data on newly added section row then it is overriding the existing content in 3rd section too.
The code is as follows:
override func viewDidLoad() {
super.viewDidLoad()
//creating section items where 1st three are static and the third section will add dynamically when the last section tapped by the user.
tableMenuItems = [["sectionHeader": "Ship To","isCollapsed":true, "image" : "shipto.png" ],
["sectionHeader": "Ship From","isCollapsed":true,"image" : "shipfrom.png"],
["sectionHeader": "Package Details","isCollapsed":true,"image" : "package.png"],
["sectionHeader": "Add a Package","isCollapsed":true,"image" : "newaddpack.png"]]
}
func numberOfSections(in tableView: UITableView) -> Int {
return tableMenuItems.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let isCollapsed = tableMenuItems[section]["isCollapsed"] as! Bool
return isCollapsed ? 0 : 1
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: tableView.frame.size.width, height: 120))
view.tag = 200 + section
view.backgroundColor = .white
let prefixIconBtn = UIButton.init(frame: CGRect.init(x: 12, y: 20, width: 48, height: 48))
prefixIconBtn.setImage(UIImage.init(named: tableMenuItems[section]["image"] as! String), for: .normal)
prefixIconBtn.tag = 500 + section
prefixIconBtn.addTarget(self, action: #selector (displayOrHideSection(_sender:)), for: .touchUpInside)
let titleLabel = UILabel.init(frame: CGRect.init(x: 75, y: 18, width: 250, height: 60))
titleLabel.tag = 100 + section
let sectionHeaderBtn = UIButton.init(frame: titleLabel.frame)
sectionHeaderBtn.addTarget(self, action: #selector (displayOrHideSection(_sender:)), for: .touchUpInside)
sectionHeaderBtn.tag = section
titleLabel.font = UIFont(name:"SFUIText-Regular", size: 20.0)
let plusOrMinus = UIButton.init(frame: CGRect.init(x: 340, y: 20, width: 38, height: 38))
plusOrMinus.tag = section
plusOrMinus.addTarget(self, action: #selector (displayOrHideSection(_sender:)), for: .touchUpInside)
titleLabel.text = tableMenuItems[section]["sectionHeader"] as? String
if titleLabel.text == "Add a Package" {
plusOrMinus.isHidden = true
}
if tableMenuItems[section]["isCollapsed"] as! Bool == true
{
plusOrMinus.setBackgroundImage(UIImage.init(named: "Log_plus"), for: .normal)
tableMenuItems[section]["isCollapsed"] = true
}
else
{
plusOrMinus.setBackgroundImage(UIImage.init(named: "Log_minus"), for: .normal)
tableMenuItems[section]["isCollapsed"] = false
}
view.addSubview(titleLabel)
view.addSubview(prefixIconBtn)
view.addSubview(sectionHeaderBtn)
view.addSubview(plusOrMinus)
plusOrMinus.tag = section
let seperatorLine = UILabel.init(frame: CGRect.init(x: 78, y: 78, width: tableView.frame.size.width, height: 1))
seperatorLine.backgroundColor = UIColor.lightGray
view.addSubview(seperatorLine)
return view
}
#objc func displayOrHideSection( _sender : AnyObject)
{
let section = _sender.tag
if section == 0
{
if tableMenuItems[0]["isCollapsed"] as! Bool == false
{
tableMenuItems[0]["isCollapsed"] = true
}
else
{
tableMenuItems[0]["isCollapsed"] = false
}
}
else if section == 1
{
if tableMenuItems[1]["isCollapsed"] as! Bool == false
{
tableMenuItems[1]["isCollapsed"] = true
}
else
{
tableMenuItems[1]["isCollapsed"] = false
}
}
else if section == 2
{
if tableMenuItems[2]["isCollapsed"] as! Bool == false
{
tableMenuItems[2]["isCollapsed"] = true
}
else
{
tableMenuItems[2]["isCollapsed"] = false
}
}
else if section! >= 3
{
if section! > 502
{
// to delete a dynamically added package
tableMenuItems.remove(at: section! - 500)
}
else if section! < 502
{
// adding a new package section when the user taps on Add a package section(Last section)
let titleLabel = self.logTable?.viewWithTag(100 + section!) as! UILabel
print(titleLabel)
// when add a package tapped
if titleLabel.text == "Add a Package"
{
tableMenuItems.insert(["sectionHeader": "Package Details","isCollapsed":true,"image" : "package.png"], at: tableMenuItems.count-1)
}
// when plus or minus button tapped on Add a package
else
{
if tableMenuItems[section!]["isCollapsed"] as! Bool == false
{
// expanding section
tableMenuItems[section!]["isCollapsed"] = true
tableMenuItems[section!]["image"] = "package.png"
}
else
{
// collapsing section
tableMenuItems[section!]["isCollapsed"] = false
tableMenuItems[section!]["image"] = "newdeletepackage.png"
}
}
}
}
logTable!.reloadData()
}

Related

create a if statement where only one button at a time can have a border

I want my swift code to use a if statement or another sequence to only display a border on one of the buttons if click at a time. So a border can only be seen on one button at a time that button would be the last one pressed. I know I could say layer.border with 0 on each button that should be selected but I want to see if there is a more efficient way to do this.
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
ba.addTarget(self, action: #selector(pressa), for: .touchDown)
bb.addTarget(self, action: #selector(pressb), for: .touchDown)
bc.addTarget(self, action: #selector(pressc), for: .touchDown)
}
#objc func pressa(){
ba.layer.borderWidth = 2
}
#objc func pressb(){
bb.layer.borderWidth = 2
}
#objc func pressc(){
bc.layer.borderWidth = 2
}
}
you can add target to all buttons at the forEach and be only one method as #Sh_Khan mention
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}
}
See how you have used an array in [ba,bb,bc].forEach { ... } to reduce code duplication? Using arrays is the key. Rather than putting the three buttons in an array inline like that, create a property instead:
var buttons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
buttons = [ba,bb,bc]
buttons.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(buttonPressed), for: .touchDown)
}
...
}
I have used the same selector buttonPressed for all three buttons. buttonPressed can accept a parameter of type UIButton, that tells us which button is pressed:
#objc func buttonPressed(_ sender: UIButton) {
buttons.forEach { ba.layer.borderWidth = 0 } // deselect all buttons first...
sender.layer.borderWidth = 2 // select the tapped button
}
If you have more than 3 buttons to manage, I suggest you don't use UIButtons at all. You should use a UICollectionView. (Learn how to use them) This view will handle the selection for you. It also allows scrolling when there's not enough space to show all the buttons. You just need to create a custom UICollectionViewCell and override its isSelected property:
override var isSelected: Bool {
didSet {
if isSelected {
self.layer.borderWidth = 2
} else {
self.layer.borderWidth = 0
}
}
}
It could be 1 method like this
[ba,bb,bc].forEach { $0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown) }
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}

How to purge tableview cells before cellforRowAt is called?

I have the following code
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
//creating a cell using the custom class
if (indexPath.row == 0 && indexPath.section == 0) {
var cell:UITableViewCell;
cell = tableView.dequeueReusableCell(withIdentifier: "book", for: indexPath)
cell.selectionStyle = UITableViewCell.SelectionStyle.none;
self.ref.observe(DataEventType.value, with: { (snapshot) in
self.ref.child("featured").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let title = value?["title"] as? String ?? ""
self.featured = value?["url"] as? String ?? ""
let label = UILabel(frame: CGRect(x: 0, y: 0, width: cell.frame.size.width, height: 24))
label.numberOfLines = 0
label.font = UIFont(name: "CeraPro-Medium", size: 20);
label.textColor = UIColor.black
label.text = NSLocalizedString(" Free Daily Pick", comment: "featured")
label.adjustsFontForContentSizeCategory = true
cell.addSubview(label)
if let image = value?["image"] as? String, image.isEmpty {
print("String is nil or empty.")
} else {
if let image = value?["image"] {
let imageView = UIImageView();
imageView.frame = CGRect(x: 0,
y: 75,
width: cell.frame.size.width,
height: 150)
imageView.contentMode = .scaleAspectFill
imageView.isUserInteractionEnabled = false
imageView.sd_setImage(with: URL(string: image as! String), placeholderImage: nil)
cell.addSubview(imageView)
let label = UILabel(frame: CGRect(x: 10,
y: cell.frame.size.height-60,
width: cell.frame.size.width,
height: 50));
label.textColor = UIColor.black
label.backgroundColor = UIColor.init(red: 1, green: 1, blue: 1, alpha: 0.5)
label.font = UIFont(name: "CeraPro-Bold", size: 16)
label.text = " \(title)"
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: cell.frame.size.width, height: 150);
view.tag = 123
view.addSubview(imageView)
view.addSubview(label)
cell.contentView.addSubview(view)
}
}
// ...
}) { (error) in
print(error.localizedDescription)
}
})
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "book", for: indexPath) as! BookTableViewCell
cell.cellIndex = indexPath
cell.dataSource = self
cell.delegate = self
cell.backgroundColor = UIColor.white
var books = [Book]();
let count = self.enterpriseBooks.count;
if count > 0 && indexPath.section <= self.enterpriseBooks_2.count {
books = self.enterpriseBooks_2[indexPath.section - 1]!;
}
if (indexPath.section == (count + 1)) {
books = nytbooks;
} else if (indexPath.section == (count + 2)) {
books = trendingbooks;
} else if (indexPath.section == (count + 3)) {
books = newbooks
}
if (books.count > 0) {
if (cell.collectionView === nil) {
cell.addCollectionView();
cell.collectionView.reloadData()
}
}
return cell
}
}
Here for section 0, row 0 - I show a featured image
For section 1, 2, 3 - I show a horizontal collection view of book images.
What's happening is by using dequeue function to get the table view cell, the table view is caching some table view cell content which overlaps each other. Is there a way to purge table view cells before cellForRowAt is called?
Programmatically adding subviews to a cell seems a terrible idea to me. Try to use auto layout and xib files whenever possible.
Anyway, you theoretically could remove all the subviews from a dequeued cell and then adding them again.
for view in cell.subviews {
view.removeFromSuperview()
}
That said, there are better approaches to use a special cell in the first position, like using different cell types.
well, to handle caching data you have to handle all else cases to assign appropriate values to UI Elements in case of nil/ failure scenarios. For an instance you have to assign nil to UIImageView's image property if image url is nil/ empty like this
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
//creating a cell using the custom class
if (indexPath.row == 0 && indexPath.section == 0) {
var cell:UITableViewCell;
cell = tableView.dequeueReusableCell(withIdentifier: "book", for: indexPath)
cell.selectionStyle = UITableViewCell.SelectionStyle.none;
self.ref.observe(DataEventType.value, with: { (snapshot) in
self.ref.child("featured").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let title = value?["title"] as? String ?? ""
self.featured = value?["url"] as? String ?? ""
let label = UILabel(frame: CGRect(x: 0, y: 0, width: cell.frame.size.width, height: 24))
label.numberOfLines = 0
label.font = UIFont(name: "CeraPro-Medium", size: 20);
label.textColor = UIColor.black
label.text = NSLocalizedString(" Free Daily Pick", comment: "featured")
label.adjustsFontForContentSizeCategory = true
cell.addSubview(label)
let imageView = UIImageView();
if let image = value?["image"] as? String, image.isEmpty {
print("String is nil or empty.")
imageView.image = nil
// same goes for the label
} else {
if let image = value?["image"] {
imageView.frame = CGRect(x: 0,
y: 75,
width: cell.frame.size.width,
height: 150)
imageView.contentMode = .scaleAspectFill
imageView.isUserInteractionEnabled = false
imageView.sd_setImage(with: URL(string: image as! String), placeholderImage: nil)
cell.addSubview(imageView)
let label = UILabel(frame: CGRect(x: 10,
y: cell.frame.size.height-60,
width: cell.frame.size.width,
height: 50));
label.textColor = UIColor.black
label.backgroundColor = UIColor.init(red: 1, green: 1, blue: 1, alpha: 0.5)
label.font = UIFont(name: "CeraPro-Bold", size: 16)
label.text = " \(title)"
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: cell.frame.size.width, height: 150);
view.tag = 123
view.addSubview(imageView)
view.addSubview(label)
cell.contentView.addSubview(view)
}
}
// ...
}) { (error) in
print(error.localizedDescription)
}
})
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "book", for: indexPath) as! BookTableViewCell
cell.cellIndex = indexPath
cell.dataSource = self
cell.delegate = self
cell.backgroundColor = UIColor.white
var books = [Book]();
let count = self.enterpriseBooks.count;
if count > 0 && indexPath.section <= self.enterpriseBooks_2.count {
books = self.enterpriseBooks_2[indexPath.section - 1]!;
}
if (indexPath.section == (count + 1)) {
books = nytbooks;
} else if (indexPath.section == (count + 2)) {
books = trendingbooks;
} else if (indexPath.section == (count + 3)) {
books = newbooks
}
if (books.count > 0) {
if (cell.collectionView === nil) {
cell.addCollectionView();
cell.collectionView.reloadData()
}
}
return cell
}
}
In all the nil cases you are doing nothing with related UI element and that allows dequeueReusability to retain old content. Just assign nil or default value to UI elements in else / nil case, it will resolve your issue

BackgroundView for section in the tableView

I use this function to add background view in my TableView when it's empty.
func emptyTableViewMessage(with message: String) {
let messageLabel: UILabel = UILabel(frame: CGRect(x: todoTableView.bounds.size.width/2, y: todoTableView.bounds.size.height / 2 , width: todoTableView.bounds.size.width, height: todoTableView.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = UIColor.gray
messageLabel.font = UIFont(name: "Open Sans", size: 15)
messageLabel.textAlignment = .center
todoTableView.backgroundView = messageLabel
}
I call it in numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearching {
if searchedData?.count == 0 {
emptyTableViewMessage(with: "Not found")
} else {
emptyTableViewMessage.backgroundView = .none
}
return (searchedData?.count)!
}
But if I have few section, For example one of them is empty, how Can I add this kind of backgroundView inside the section (not in the header, because I have different items in the header)
Many thanks
First, you could created an empty cell prototype, this one will be use when there is no data to show. Then let's modify your emptyTableViewMessage method to return a UILabel.
func emptyTableViewMessage(with message: String) -> UIView {
let messageLabel: UILabel = UILabel(frame: CGRect(x: table.bounds.size.width/2, y: table.bounds.size.height / 2 , width: table.bounds.size.width, height: table.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = UIColor.gray
messageLabel.font = UIFont(name: "Open Sans", size: 15)
messageLabel.textAlignment = .center
return messageLabel
}
In numberOfRowsInSection when there is no data to show lets return 1 (to display the Not Found text). If there is data to show let's return how many rows.
guard let dataFound = searchedData?.count else { return 1 }
return dataFound > 0 ? dataFound : 1
Finally in cellForRowAt you dequeue the appropriate prototype cell based on searchData?.count.
let notFound = searchData?.count > 0 ?? false
guard let cell = tableView.dequeueReusableCell(withIdentifier: notFound ? "NotFoundCellId" : "CellId") else { return UITableViewCell() }
if notFound {
cell.backgroundView = emptyTableViewMessage(with: "Not Found")
} else {
cell.textLabel?.text = countries[indexPath.row]
}
I quickly created a small project to test this and seems to work fine, you can check it at Github. The project doesn't manage the data how it should be done for a production code, the search bar doesn't displays well on simulator for iPhone X and newer, etc.; it's just a quick sample on how to display the Not Found text in a table view with many sections so you can have an idea how to do it.
Some screenshots:
No filter
Filtered by "ica"
Filtered by "an"
Hope this helps you!

TableView rows dissapear when tapped

I have a table view embedded in an iCarousel item and firstly I can't seem to set the view frame whether it's in viewDidLayoutSubviews() but also when I tap the Tableview all the cells disappear. I have created the Tableview in its own ViewController as each carousel item will need editing buttons for the Tableviews they contain. If I don't put the Tableview in its own view controller the cells remain but I get an index out of range when scrolling a lot of views and I can't add editing buttons. I've run out of ideas and would love some pointers or tips.
class BillSplitterTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
var splitter: BillSplitter?
var tableView: UITableView!
var viewFrame: CGRect?
init(frame: CGRect, splitter: BillSplitter) {
super.init(nibName: nil, bundle: nil)
self.splitter = splitter
self.viewFrame = frame
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:)")
}
override func viewDidLoad() {
super.viewDidLoad()
let subView = UIView(frame: viewFrame!)
view.backgroundColor = .clear
tableView = UITableView(frame: view.frame)
tableView.delegate = self
tableView.dataSource = self
tableView.frame = subView.frame
tableView.separatorStyle = .none
tableView.estimatedRowHeight = 35
let tableViewBackground = UIImageView(image: UIImage(data: splitter?.image as! Data, scale:1.0))
tableViewBackground.contentMode = .scaleAspectFit
tableViewBackground.frame = tableView.frame
tableView.backgroundView = tableViewBackground
subView.addSubview(tableView)
view.addSubview(subView)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (splitter?.items?.count)! > 0 {
return (splitter!.items?.count)!
} else {
TableViewHelper.EmptyMessage("\(splitter?.name!) has no items to pay for.", tableView: tableView)
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(SplitterCarouselItemTableViewCell.classForCoder(), forCellReuseIdentifier: "splitterCarouselItemTableViewCell")
let cell: SplitterCarouselItemTableViewCell = tableView.dequeueReusableCell(withIdentifier: "splitterCarouselItemTableViewCell") as! SplitterCarouselItemTableViewCell
let item = (splitter?.items?.allObjects as! [Item])[indexPath.row]
let count = item.billSplitters?.count
cell.backgroundColor = UIColor(netHex: 0xe9edef).withAlphaComponent(0.5)
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
}
}
the carousel viewcontroller:
func numberOfItems(in carousel: iCarousel) -> Int {
return allBillSplitters.count
}
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
let splitter = allBillSplitters[index]
carouselIndex = index
let splitterView = SplitterCarouselItemView(frame: CGRect(x: 0, y: 0, width: width, height: height))
let viewWidth = Int(splitterView.frame.width)
let nameLabel = SplitterCarouselItemNameLabel(frame: CGRect(x: 0, y: 0, width: viewWidth, height: 30))
let emailLabel = SplitterCarouselItemEmailLabel(frame: CGRect(x: 0, y: 30, width: viewWidth, height: 20))
let addItemButton = SplitterCarouselItemButton(frame: CGRect(x: viewWidth - 45, y: 50, width: 35, height: 35), addsItem: true)
let deleteItemButton = SplitterCarouselItemButton(frame: CGRect(x: 15, y: 50, width: 35, height: 35), addsItem: false)
let itemHeight = Int(splitterView.frame.height)
let payTotalButton = SplitterCarouselItemPayButton(frame: CGRect(x: 0, y: itemHeight - 35, width: viewWidth + 1, height: 35))
let tableViewHeight = Int(height - 130)
let frame = CGRect(x: 0, y: 85, width: viewWidth, height: tableViewHeight)
let tableView = BillSplitterTableView(frame: frame, splitter: splitter)
splitterView.addSubview(nameLabel)
splitterView.addSubview(emailLabel)
splitterView.addSubview(addItemButton)
splitterView.addSubview(deleteItemButton)
splitterView.addSubview(tableView.view)
splitterView.addSubview(payTotalButton)
nameLabel.text = "\(allBillSplitters[index].name!)"
emailLabel.text = "\(allBillSplitters[index].email!)"
payTotalButton.setTitle("Pay £\(allBillSplitters[index].total)", for: .normal)
return splitterView
}
func carousel(_ carousel: iCarousel, valueFor option: iCarouselOption, withDefault value: CGFloat) -> CGFloat {
if allBillSplitters.count > 2 {
switch option {
case .spacing:
return value * 1.05
case .fadeMin:
return 0.0
case .fadeMinAlpha:
return 0.3
case .fadeMax:
return 0.0
default:
return value
}
}
return value
}
Thanks for any help in advance

TableView in iCarousel finding nil while unwrapping optional value

So, I have a carousel of "BillSplitters" and on each carousel item it should display the uniques items a BillSplitter is having. So I'm getting fatal error: unexpectedly found nil while unwrapping an Optional value Normally i can slowly hone in on an error like this i find the issue but when following on from a breakpoint line by line it enters into the iCarousel code which i cant follow. Im also sure theres nothing going wrong in i carousel as if i dont addSubview(tableView) then it runs fine. It also seems to create the first couple of tableviews and add them fine and then gets the error. Here is the code im using:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let splitter = allBillSplitters[carouselIndex]
if (splitter.items?.count)! > 0 {
return (splitter.items?.count)!
} else {
TableViewHelper.EmptyMessage("\(splitter.name!) has no items to pay for.\nGo back to assign some items to their name.", tableView: tableView)
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ItemCell = tableView.dequeueReusableCell(withIdentifier: "SplitterItemCell") as! ItemCell
let itemsSet = allBillSplitters[carouselIndex].items
let items = itemsSet?.allObjects as! [Item]
let item = items[indexPath.row]
let count = item.billSplitters?.count
if count! > 1 {
cell.name!.text = "\(item.name!) split \(count!) ways"
cell.price!.text = "£\(Double(item.price)/Double(count!))"
} else {
cell.name!.text = item.name!
cell.price!.text = "£\(item.price)"
}
return cell
}
func numberOfItems(in carousel: iCarousel) -> Int {
return allBillSplitters.count
}
I've read in a few places that I should remove the if let view = view statement in the following function as it's not re-using the items and always creating new ones. If I leave it in I get the same error immediately when creating the first carousel item and when I remove it, it happens on the creating the third i carousel item.
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
carouselIndex = index
var splitterView: UIImageView
var nameLabel: UILabel
var emailLabel: UILabel
var totalLabel: UILabel
var tableView: UITableView
let splitter = allBillSplitters[index]
//reuse view if available, otherwise create a new view
if let view = view as? UIImageView {
splitterView = view
//get a reference to the label in the recycled view
nameLabel = splitterView.viewWithTag(1) as! UILabel
emailLabel = splitterView.viewWithTag(2) as! UILabel
totalLabel = splitterView.viewWithTag(3) as! UILabel
tableView = splitterView.viewWithTag(4) as! UITableView
} else {
let height = carousel.contentView.frame.height - 85
let width = carousel.contentView.frame.width - 80
//don't do anything specific to the index within
//this `if ... else` statement because the view will be
//recycled and used with other index values later
splitterView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
splitterView.layer.cornerRadius = 10
splitterView.clipsToBounds = true
splitterView.image = UIImage(data: splitter.image as! Data, scale:1.0)
splitterView.contentMode = .scaleAspectFit
splitterView.backgroundColor = UIColor(netHex: 0xCA9875)
let viewWidth = Int(splitterView.frame.width)
nameLabel = UILabel(frame: CGRect(x: 5, y: 0, width: viewWidth, height: 30))
nameLabel.backgroundColor = .clear
nameLabel.backgroundColor?.withAlphaComponent(0.1)
nameLabel.textAlignment = .left
nameLabel.font = nameLabel.font.withSize(20)
nameLabel.tag = 1
emailLabel = UILabel(frame: CGRect(x: 5, y: 30, width: viewWidth, height: 15))
emailLabel.backgroundColor = .clear
emailLabel.textAlignment = .left
emailLabel.font = emailLabel.font.withSize(15)
emailLabel.tag = 2
totalLabel = UILabel(frame: CGRect(x: 5, y: 45, width: viewWidth, height: 15))
totalLabel.backgroundColor = .clear
totalLabel.textAlignment = .left
totalLabel.font = totalLabel.font.withSize(15)
totalLabel.tag = 3
let tableViewHeight = height - 65
let frame = CGRect(x: 0, y: 65, width: width, height: tableViewHeight)
tableView = UITableView(frame: frame)
tableView.delegate = self
tableView.dataSource = self
tableView.tag = 4
totalLabel.backgroundColor = .clear
splitterView.addSubview(nameLabel)
splitterView.addSubview(emailLabel)
splitterView.addSubview(totalLabel)
splitterView.addSubview(tableView)
}
//set item label
//remember to always set any properties of your carousel item
//views outside of the `if (view == nil) {...}` check otherwise
//you'll get weird issues with carousel item content appearing
//in the wrong place in the carousel
nameLabel.text = "\(allBillSplitters[index].name!)"
emailLabel.text = "\(allBillSplitters[index].email!)"
totalLabel.text = "£\(allBillSplitters[index].total)"
return splitterView
}
func carousel(_ carousel: iCarousel, valueFor option: iCarouselOption, withDefault value: CGFloat) -> CGFloat {
switch option {
case .spacing:
return value * 1.2
case .fadeMin:
return 0.0
case .fadeMinAlpha:
return 0.3
case .fadeMax:
return 0.0
default:
return value
}
}
I've looked all over and can't find a solution so any help would be great. Thanks
I'm an idiot. Forgot the following:
tableView.register(CarouselTableViewCell.classForCoder(), forCellReuseIdentifier: "carouselTableViewCell")
in tableviews cellForRowAt function