How to Use Table View Cell Outside Table View Function - swift

i need to change my image in cell using view will appear. But i can't use my cell in view will appear here what i've done
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
let cell: HomeCellTableViewCell = self.tableCity.dequeueReusableCell(withIdentifier: "Cell") as! HomeCellTableViewCell
if session == nil {
print("first")
cell.iconForDownload.setImage(UIImage(named: "ic_download"), for: .normal)
} else {
print("second")
cell.iconForDownload.setImage(UIImage(named: "ic_next_green"), for: .normal)
}
}
it print "first" but the image still didn't change
in my cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: HomeCellTableViewCell = self.tableCity.dequeueReusableCell(withIdentifier: "Cell") as! HomeCellTableViewCell
let city = listData[indexPath.row]
cell.labelNameCity.text = city.region
cell.labelNameCityJpn.text = city.regionJpn
let stringImage = config.BASE_URL+city.imgArea
let url = URL(string: stringImage.replacingOccurrences(of: " ", with: "%20"))
urlDownload = config.BASE_URL+kota.urlDownload
urlDownloadFinal = URL(string: urlDownload.replacingOccurrences(of: " ", with: "%20"))
if session == nil {
cell.imageCity.kf.setImage(with: url)
cell.iconForDownload.setImage(UIImage(named: "ic_download"), for: .normal)
} else {
cell.imageCity.kf.setImage(with: url)
cell.iconForDownload.setImage(UIImage(named: "ic_next_green"), for: .normal)
}
return cell
}

You need to use cellForRow(at:) to get the cell it will return optional UITableViewCell so use if let or guard let to wrapped the optional.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
let indexPath = IndexPath(row: 0, section: 0) //Set your row and section
if let cell = tableView.cellForRow(at: indexPath) as? HomeCellTableViewCell {
//access cell
}
}
Note: The batter approach is to set your datasource array and simply reload the affected table rows using reloadRows(at:with:).
let indexPath = IndexPath(row: 0, section: 0) //Set your row and section
self.tableView.reloadRows(at: [indexPath], with: .automatic)

Related

UITapGestureRecognizer has no member tag

In this case i need my uiLabel clickable, so i add uiTapRecognizer. Then, i need to send the indexpath section to the tag so i can get the choosen section.
But when i try to get the sender.tag in tapFunction() there is error : UITapGestureRecognizer has no member tag
If i change the sender to uiButton -> func tapFunction(_ sender:uiButton) {
it doesn't print anything inside the function
Is there any workaround for this case (still using uilabel not change it to uibutton)?
Can i also send data when #selector tapfunciton(_: , data:"text")
and read it in tapFunction(data:String)? If this possible, can someone show it ?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LoanCell", for: indexPath) as? LoanCell else {
return UITableViewCell()
}
let item = viewModel.itemsForRowAt(indexPath: indexPath)
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapFunction(_:)))
cell.syaratLabel.tag = indexPath.section
cell.isUserInteractionEnabled = true
cell.syaratLabel.addGestureRecognizer(tap)
return cell
}
#objc
func tapFunction(_ sender:UITapGestureRecognizer) {
print("successprint")
let item = viewModel.itemsForRowAt(indexPath: IndexPath(row: 0, section: sender.tag))
let viewController = DetilSandKViewController(messageSnK:item.prdAgreementDetail, titleSnK:item.nomorKontrak)
viewController.modalPresentationStyle = .overCurrentContext
viewController.modalTransitionStyle = .crossDissolve
present(viewController, animated: true, completion: nil)
}

How to add label to UITableViewCells before inserting rows?

My goal is to insert a label that says "Loading..." to each visible UITableViewCell before I insert the rows into the tableview.
So far I have the following:
I have a fully functional table view that loads the correct number of cells as well as the information to go in each cell. The code is as follows:
override func viewDidAppear(_ animated: Bool) {
let attributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh", attributes: attributes)
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
MyChallengesTableView.addSubview(refreshControl) // not required when using UITableViewController
self.BetCreatorUsernameArray.removeAll()
self.BetDescriptionArray.removeAll()
self.UserMoneyInBet.removeAll()
self.BetTotalPoolArray.removeAll()
self.BetEndDateArray.removeAll()
loadData()
self.MyChallengesTableView.reloadData()
}
//refresh table view action
#objc func refresh(refreshControl:UIRefreshControl) {
// Code to refresh table view
//refreshes tableview
self.BetCreatorUsernameArray.removeAll()
self.BetDescriptionArray.removeAll()
self.UserMoneyInBet.removeAll()
self.BetTotalPoolArray.removeAll()
self.BetEndDateArray.removeAll()
loadData()
self.MyChallengesTableView.reloadData()
//stops refreshing
self.refreshControl.endRefreshing()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.BetDescriptionArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyChallengesCell", for: indexPath) as! MyChallengesTableViewCell
cell.BetDescriptionOutlet.text = BetDescriptionArray[indexPath.row]
cell.UserMoneyInBetOutlet.text = ("Money In: $" + UserMoneyInBet[indexPath.row])
cell.TotalBetPoolOutlet.text = ("Pool: $" + BetTotalPoolArray[indexPath.row])
cell.BetEndDateOutlet.text = ("End Date: " + BetEndDateArray[indexPath.row])
cell.BetCreatorUsername = BetCreatorUsernameArray[indexPath.row]
cell.betInformation = ["BetDescription" : BetDescriptionArray[indexPath.row],
"BetAmount" : UserMoneyInBet[indexPath.row],
"TotalMoneyInBet" : BetTotalPoolArray[indexPath.row],
"BetEndDate" : BetEndDateArray[indexPath.row],
"BetCreatorUsername" : BetCreatorUsernameArray[indexPath.row]]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(152)
}
#objc func loadData(){
//get username of current user
self.databaseRef.child("Users").child((Auth.auth().currentUser?.uid)!).child("Username").observeSingleEvent(of: .value, with: {(snapshot) in
self.currentUserUsername = snapshot.value as! String
//make dictionary for each bet that a user is in
self.databaseRef.child("UserBets").child(self.currentUserUsername).observeSingleEvent(of: .value, with: {(snapshot1) in
if snapshot1.exists(){
let UserBetsDictionary = snapshot1.value as! NSDictionary
for (Bet, _) in UserBetsDictionary {
//get bet information from the bets in database
self.databaseRef.child("Bets").child((Bet as? String)!).child("BetInfo").observeSingleEvent(of: .value, with: {(snapshot2) in
let BetInfoDictionary = snapshot2.value as! NSDictionary
self.BetCreatorUsernameArray.append((BetInfoDictionary["Creator Username"] as! String))
self.BetDescriptionArray.append((BetInfoDictionary["BetDescription"] as! String))
self.UserMoneyInBet.append((BetInfoDictionary["BetAmount"] as! String))
self.BetTotalPoolArray.append((BetInfoDictionary["TotalMoneyInBet"] as! String))
self.BetEndDateArray.append((BetInfoDictionary["BetEndTime"] as! String))
//insert the rows
if self.BetCreatorUsernameArray.count != 0 {
self.MyChallengesTableView.insertRows(at: [IndexPath(row:self.BetCreatorUsernameArray.count-1, section:0)], with: UITableViewRowAnimation.automatic)
}
})
}
}
})
})
}
I'm not sure if the code matters too much at this point. In short, I want to add a label before running the function "loadData" in ViewDidAppear and then hide the label once the function is ran and the rows are inserted.
I think it will suit you, just make an extension like this:
extension UITableView {
func startLoading() {
let view = UIView()
separatorStyle = .none
let activityIndicatorView = UIActivityIndicatorView(style: .gray)
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(activityIndicatorView)
activityIndicatorView.startAnimating()
activityIndicatorView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -10).isActive = true
activityIndicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
let label = UILabel()
label.textColor = UIColor.lightGray
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
label.text = "Loading..."
label.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 10).isActive = true
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.font = UIFont.systemFont(ofSize: 14)
self.backgroundView = view
}
func stopLoading() {
clearTableView()
}
func clearTableView() {
separatorStyle = .singleLine
self.backgroundView = nil
}
}
And after that just invoke method startLoading on your tableView instance before request, and on result from request invoke stopLoading method.

selecting cell in section one authomatically selecting cell in section two swift

I have an application where I have two sections the issue I have now is if I select an item in section 1, it automatically selects a cell in section 2 which is not suppose to be. I want Items to be selectable in section 1 without affecting section two.
below is my selection
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0:
showCustomDialog(subD: sub[indexPath.row])
case 1:
let cell = tableView.cellForRow(at: indexPath) as! VasListCell
cell.checkBox.setOn(true, animated: true)
default: break
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 1:
let cell = tableView.cellForRow(at: indexPath) as! VasListCell
cell.checkBox.setOn(false, animated: true)
default: break
}
}
where I am using the selected index
func selectedIndex(viewcontroller: UIViewController) {
let selectedRows = tableView.indexPathsForSelectedRows
guard let vasRow = selectedRows?.map ({ vas[$0.row] }) else { return }
selectedVasData = vasRow
let vasData = selectedVasData
let subData = selectedSubData
let vcr = viewcontroller as! CheckoutVC
vcr.vas = vasData
vcr.sub = subData
let tot1 = subData.compactMap {$0.price}
let tot2 = vasData.compactMap {$0.amount}
let tot = tot1 + tot2
let reduced = tot.compactMap(Double.init).reduce(0, +)
vcr.tableView.reloadData()
self.present(viewcontroller, animated: true, completion: nil)
print("CELL INDEX vas \(StaticFunc.convertDoubleToCurrency(amount: reduced))")
}

How to select only tapped cells with button?

Good time of day, in my app i have a tableview and custom cell, in cell there are labels, button and progressBar, so that when i tap button download proceeds and progressBar shows progress, but when i scroll down i realise that there are other cells are selected and shows progress and when i scroll up again progress of my selected cell stops. Could you help, any feedbacks appreciated )
That's my TableViewController :
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return titles.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
cell.pesnya.text = titles[indexPath.row]
cell.pevets.text = artists[indexPath.row]
cell.url = urls[indexPath.row]
return (cell)
}
#IBAction func buttonPressed(_ sender: AnyObject) {
(sender as! UIButton).isSelected = !(sender as! UIButton).isSelected
if (sender as! UIButton).isSelected {
if let indexPath = tableView.indexPath(for: sender.superview!?.superview as! UITableViewCell) {
DownloadManager.shared.download(url: urls[indexPath.row], title: titles[indexPath.row])
}
} else {
// (sender as! UIButton).setTitle("Удалить", for: UIControlState.normal)
if let indexPath = tableView.indexPath(for: sender.superview!?.superview as! UITableViewCell) {
let name = "\(titles[indexPath.row]).mp3"
let name2 = name.replacingOccurrences(of: " ", with: "")
let filePathURL = URL(string:"string")
do {
try FileManager.default.removeItem(at: filePathURL!)
} catch {
print("Could not delete file: \(error)")
}
}
}
}
This is because table view cell will be reused when you scroll. Set views to initial status in cell's prepareForReuse. Like this:
class ACell: UITableViewCell {
override func prepareForReuse() {
view.hidden = true
}
}
try this :-
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int
{
return titles.count
}
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for:
indexPath) as! ViewControllerTableViewCell
cell.pesnya.text = titles[indexPath.row]
cell.pevets.text = artists[indexPath.row]
cell.url = urls[indexPath.row]
// Create IBOutlet for button
cell.btnPressed.tag = indexPath.row
cell.btnPressed.addTarget(self, action: #selector(self.buttonPressed(sender:)), for: .touchUpInside)
if cell.btnPressed.isSelected {
DownloadManager.shared.download(url: urls[indexPath.row], title: titles[indexPath.row])
}else {
let name = "\(titles[indexPath.row]).mp3"
let name2 = name.replacingOccurrences(of: " ", with: "")
let filePathURL = URL(string:"string")
do {
try FileManager.default.removeItem(at: filePathURL!)
} catch {
print("Could not delete file: \(error)")
}
}
return (cell)
}
#IBAction func buttonPressed(_ sender: UIButton) {
let indexPath = IndexPath(row: sender.tag, section: 0)
if let cell = tableView.cellForRow(at: indexPath) as?
ViewControllerTableViewCell {
cell.btnPressed.isSelected = !cell.btnPressed.isSelected
tableview.reloadData()
}
}

Button state activates on wrong cells

I added button into cell-s and added action so if user touches it then the state is "Dislike" and if user touches again the state is "Like". However, the state applies to other cell buttons also. And if I scroll fast it just randomly picks what cell button should have the state. What causes this?
I call button with function inside cellForRowAt indexPath: IndexPath function like this:
cell.likeButton.addTarget(self, action: #selector(like), for: .touchUpInside)
And this is the function that is assigned to the button:
func like(sender: UIButton){
let section = 0
let row = sender.tag
let indexPath = IndexPath(row: row, section: section)
let cell: FeedTableViewCell = tableView.dequeueReusableCell(withIdentifier: "feedCell", for: indexPath) as! FeedTableViewCell
FIRDatabase.database().reference().child("posts").child(postsArray[indexPath.row].key).runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = FIRAuth.auth()?.currentUser?.uid {
var stars : Dictionary<String, Bool>
stars = post["stars"] as? [String : Bool] ?? [:]
var starCount = post["starCount"] as? Int ?? 0
if let _ = stars[uid] {
// Unstar the post and remove self from stars
starCount -= 1
stars.removeValue(forKey: uid)
cell.likeButton.tag = indexPath.row
cell.likeButton.setTitle("Like", for: .normal)
cell.likeLabel.text = "\(starCount)"
} else {
// Star the post and add self to stars
starCount += 1
stars[uid] = true
cell.likeButton.tag = indexPath.row
cell.likeButton.setTitle("Dislike", for: .normal)
cell.likeLabel.text = "\(starCount)"
}
post["starCount"] = starCount as AnyObject?
post["stars"] = stars as AnyObject?
// Set value and report transaction success
currentData.value = post
return FIRTransactionResult.success(withValue: currentData)
}
return FIRTransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
}
}
And like this I created the tableview with cells:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: FeedTableViewCell = tableView.dequeueReusableCell(withIdentifier: "feedCell", for: indexPath) as! FeedTableViewCell
cell.likeButton.tag = indexPath.row
cell.likeButton.addTarget(self, action: #selector(self.tapped), for: .touchUpInside)
}
What causes the state to transfer to the other buttons also? I even added tags so it detects the selected button. Is there something to do with cell reuse?
It adds likes to Firebase to the right one..
This is caused by reusing previous cells when scrolling and is the base mechanism of a table view.
You need to reset the state of your button on every call to cellForRowAtIndexPath.
Between let cell = ... and cell.starButton.addTarget you need to perform something like cell.starButton.deselect(), or .select(), based on the index path you're working on.
var selectindex : Int?
var selectedindex : NSMutableArray = NSMutableArray()
#IBOutlet var tableview: UITableView!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("LikeCell", forIndexPath: indexPath)
let like: UIButton = (cell.viewWithTag(2) as! UIButton)
let comment: UIButton = (cell.viewWithTag(3) as! UIButton)
if selectedindex.containsObject(indexPath.row) {
like.setBackgroundImage(UIImage.init(named: "like.png"), forState: .Normal)
}else{
like.setBackgroundImage(UIImage.init(named: "like (1).png"), forState: .Normal)
}
comment.setBackgroundImage(UIImage(named: "chat.png"), forState: UIControlState.Normal)
like.addTarget(self, action: #selector(self.CloseMethod(_:event:)), forControlEvents: .TouchDown)
comment.addTarget(self, action: #selector(self.CloseMethod1(_:event:)), forControlEvents: .TouchDown)
return cell
}
#IBAction func CloseMethod(sender: UIButton, event: AnyObject) {
let touches = event.allTouches()!
let touch = touches.first!
let currentTouchPosition = touch.locationInView(self.tableview)
let indexPath = self.tableview.indexPathForRowAtPoint(currentTouchPosition)!
selectindex = indexPath.row
if selectedindex.containsObject(selectindex!) {
selectedindex.removeObject(selectindex!)
// call your firebase method for update database
}else{
selectedindex.addObject(selectindex!)
// call your firebase method for update database
}
self.tableview.reloadData()
}
Output :
https://www.dropbox.com/s/ub7wln5y6hdw0sz/like%20button.mov?dl=0
I think this issue is because of dequeuing your cell twice. you should try;
func like(sender: UIButton){
//your code ...
let cell: FeedTableViewCell = self.tableViewAddress.cellForRowAtIndexPath(indexPath) as! FeedTableViewCell
//Your code ...