How to make a Progress View in a TableView? - swift

I'm trying to make a Progress View on a TableView.
I succeeeded to create progressview, but if animation is true bar disappears, and if is false bar is full... i can't understand why.
This is my code:
here is my appending method:
if formazione == formazione {
guestInfoList.append("Formazione: \(formazione)%")
}
and here the full dequeue function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var countdowncell = tableView.dequeueReusableCell(withIdentifier: "countdowncell", for: indexPath)
//countdown
if indexPath.row == 9{
countdowncell.textLabel?.text = calcolaCountdown()
}else{
countdowncell.textLabel?.text = guestInfoList[indexPath.row]
}
//Creazione Progress View
if indexPath.row == 12 {
cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
let progressView = UIProgressView(progressViewStyle: .default)
//setting height of progressView
let transform = CGAffineTransform(scaleX: cell.frame.size.width, y: cell.frame.size.height)
progressView.transform = transform
cell.contentView.addSubview(progressView)
//**//inserisci valore formazione
progressView.setProgress(Float(formazione), animated: false)
}
return countdowncell
}
Ps: and if i want to put the progress View on the right Side of the cell?
Edit
Finally i made it! Thank you matt!!
if indexPath.row == 12 {
var cell = tableView.dequeueReusableCell(withIdentifier: "formprogresscell", for: indexPath)
let progressView = UIProgressView(progressViewStyle: .default)
//setting height of progressView
progressView.frame = CGRect(x: 230, y: 20, width: 130, height: 130)
progressView.progress += 0.5
progressView.rightAnchor.accessibilityActivate()
//**//inserisci valore formazione
progressView.setProgress(Float(formazione), animated: true)
progressView.progressTintColor = UIColor.red
cell.textLabel?.text = "Formazione: \(formvalue)%"
cell.contentView.addSubview(progressView)
}

The problem is what you do with the progress view after creating it:
let progressView = UIProgressView(progressViewStyle: .default)
You are failing to give the progressView any frame. Therefore it doesn't have one. In particular, it has no width. Therefore it is invisible.
Giving it a frame will solve both your problems: you'll give the view a width, and you'll give it an origin so you can put it where you want inside the content view. (But in fact you should be using autolayout, not the frame.)

Related

how to properly single out cell accessory view

So my goal is to properly separate the cells accessory. Here is my first set of cells when the segment control is at the first index. The accessory type is a normal disclosure indicator.
Now when I switch the value of the segment index, i set my cells with a custom accessory view.
Now the issue is when I switch back to the first segment, the custom accessory view comes over to the first 2 cells as well like so :
I just want to figure out how I can prevent this from happening and keep the cell accessories properly separated. I will attach my necessary code for the issue.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.CellDetails.studentDashCell, for: indexPath)
let cellImage = UIImage(systemName: "checkmark.seal.fill")
let cellImageView = UIImageView(image: cellImage)
cellImageView.tintColor = #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)
cellImageView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
if selectorOfEvents.selectedSegmentIndex == 0 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.consistentFont, size: 22)
cell.textLabel?.text = gothereEvents[indexPath.row].eventName
cell.accessoryType = .disclosureIndicator
} else if selectorOfEvents.selectedSegmentIndex == 1 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.menuTitleFont, size: 22)
cell.textLabel?.text = gotherePurchasedEventNames[indexPath.row].purchasedEventName
cell.accessoryView = cellImageView
}
return cell
}
The cellForRowAt() method. ^
#IBAction func eventsSegmented(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
navigationItem.title = "Events"
tableView.reloadData()
} else {
navigationItem.title = "Purchased Events"
tableView.reloadData()
}
}
The IBAction func for the segmented control. Thanks in advance.
My suspicion is that the accessory view / type is getting carried over on some dequeued cells. You should explicitly set both the accessoryView and cell.accessoryType type in both cases:
if selectorOfEvents.selectedSegmentIndex == 0 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.consistentFont, size: 22)
cell.textLabel?.text = gothereEvents[indexPath.row].eventName
cell.accessoryType = .disclosureIndicator
cell.accessoryView = nil //<-- here
} else if selectorOfEvents.selectedSegmentIndex == 1 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.menuTitleFont, size: 22)
cell.textLabel?.text = gotherePurchasedEventNames[indexPath.row].purchasedEventName
cell.accessoryView = cellImageView
cell.accessoryType = .none //<-- here
}

Cached shadow path for UITableView

I want to cache a shadow path to make the performance of UITableView better.
I've read https://yalantis.com/blog/mastering-uikit-performance/ that
if let rect = cell.imageView?.bounds {
cell.imageView?.layer.shadowPath = UIBezierPath(rect: rect).cgPath
}
would stop offscreen shadows, however using the following function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Cell data: \(data[indexPath.row])"
if let rect = cell.imageView?.bounds {
cell.imageView?.layer.shadowPath = UIBezierPath(rect: rect).cgPath
}
cell.imageView?.layer.shadowRadius = 8
cell.imageView?.layer.shadowOffset = CGSize(width: 3, height: 3)
cell.imageView?.layer.shadowOpacity = 0.5
cell.imageView?.layer.cornerRadius = 20
cell.imageView?.image = UIImage(named: "PlaceholderImage")
return cell
}
produces a result where some cells have the shadow and some don't.
How can I implement caching of shadow paths to all cells in a simple UITableView
The problem is these lines:
if let rect = cell.imageView?.bounds {
cell.imageView?.layer.shadowPath = UIBezierPath(rect: rect).cgPath
}
If the cell's imageView has never been assigned an image, its frame is .zero and so its bounds is .zero. So the shadowPath ends up with a cgPath that is just a point, and no shadow appears.
The way I would solve this is that I would not use the built-in imageView property at all. I'd use a custom cell class with my own image view whose frame I can control, instead of the built-in imageView which plays these sorts of tricks.
if let shadowPath = self.shadowPathCache {
cell.imageView?.layer.shadowPath = shadowPath
} else if let rect = cell.imageView?.bounds {
self.shadowPathCache = UIBezierPath(rect: rect).cgPath
cell.imageView?.layer.shadowPath = self.shadowPathCache
}

TableView Cell view shadow does not coming properly in Swift

I have added view as a background for tableview cell and I am giving shadow for view. Here when I run tableview shadow does not coming properly, once I scroll down and up then shadow coming properly.
I have given shadow according to this answer answer
Code:
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -1, height: 1)
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PlansTableViewCell
cell.containerView.dropShadow()
return cell
}
Before scrolling shadow coming like this:
after scrolling coming like below:
After running(before scrolling) also i need second image kind of output, Here Help me with the code.
Problem is here
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
bounds isn't correct at that time , try
call it from
override func layoutSubviews() {
super.layoutSubviews()
self.containerViewdropShadow()
}
or
cell.layoutIfNeeded()
cell.containerView.dropShadow()

UICollection Cells are mixed in swift

I'm trying to generate cells and put labels inside of it. However, when i scroll down labels got mixed between cells. Here is my code and i'm trying to solve it.
let lblTitle = UILabel()
let lblMetro = UILabel()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: indexPath) as? UICustomCollectionViewCell
if indexPath.row == 0 {
lblTitle.frame = CGRect(x: 0, y: 0, width: 195, height: 40)
lblTitle.font = UIFont.systemFont(ofSize: 14)
lblTitle.textColor = UIColor.white
lblTitle.backgroundColor = colorLiteral(red: 0.2122299671, green: 0.4379466176, blue: 0.8993332386, alpha: 1)
lblTitle.text = " 1”
cell?.contentView.addSubview(lblTitle)
}
if indexPath.row == 1 {
lblMetro.frame = CGRect(x: 55, y: 290, width: 100, height: 20)
lblMetro.font = UIFont.boldSystemFont(ofSize: 17)
lblMetro.textColor = colorLiteral(red: 0, green: 0.3117707968, blue: 0.5609284043, alpha: 1)
lblMetro.text = “2”
cell?.contentView.addSubview(lblMetro)
}
return cell ?? UICollectionViewCell()
}
}
Here cells are dequeued
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: indexPath) as? UICustomCollectionViewCell
so you might get a 1 with previously added label , you need to clear them after dequeue , it would be messy but it's better to isolate the vc's labels from the cells 1 so add them inisde the cell configure or make them an outlets , to remove give them a tag and after the above line do
cell.contentView.subviews.forEach {
if $0.tag == 200 {
$0.removeFromSuperview()
}
}
Not optimised but this might solve it, remove the subview from superview before adding it:
cell?.contentView.lblTitle.removeFromSuperview()
cell?.contentView.addSubview(lblTitle)
And:
cell?.contentView.lblMetro.removeFromSuperview()
cell?.contentView.addSubview(lblMetro)
I suggest using a very rarely used method of UICollectionViewCell or UITableViewCell prepareForReuse.
In definition of UICustomCollectionViewCell insert the function:
class UICustomCollectionViewCell: UICollectionViewCell {
func prepareForReuse() {
// This method is immediately called when a cell is about to be dequeued.
super.prepareForReuse()
if let view = contentView.viewWithTag(100) {
view.removeFromSuperView()
}
if let view = contentView.viewWithTag(101) {
view.removeFromSuperView()
}
}
}
Then give tags to the labels
lblMetro.tag = 100
lblTitle.tag = 101
This solution is efficient if you only use a limited labels and cells. For a more generic approach create labels dynamically and share the tag. In the prepareForReuse() just remove subview with that tag.

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