Previous/Next Functionality like iOS mail app? - ios

I'm really close to figuring this out. So in the iOS mail app when you click on the two arrow keys it takes you to the previous/next mail. Its on the top right
I've managed to pass the indexPath value to my second viewcontroller and print in in the console. I can also increase and decrease from it.
if segue.identifier == "DetailVC" {
let detailVC = segue.destination as! DetailVC
let indexPath = self.collectionViewIBO.indexPathsForSelectedItems?.last!
detailVC.index = indexPath
}
EDIT
This is where I'm pulling the data from. It reads the values from my model. I cannot assign an indexPath to it however. I can only do that from the previous view controller
var monster: Monsters!
I've attempted to implement the "previous" functionality using this code. My view styling are in the displayDataForIndexPath() function and the function is called from my view will appear
#IBAction func monsPreviousIBO(_ sender: Any) {
self.index = IndexPath(row: self.index.row - 1, section: self.index.section)
displayDataForIndexPath()
}
But all it does is decrease the IndexPath. For some reason the data doesn't actually reload with my function. I'm missing some important puzzle piece here to achieving the same functionality.
EDIT The code in my displayDataForIndex is as follows
func displayDataForIndexPath() {
if index.row == 0 {
self.monsPreviousIBO.removeFromSuperview()
}
var monsterName = (String(format: "%03d", monster.speciesId!))
self.navigationItem.title = monster.name!
let gif = UIImage(gifName: monsterName)
self.gifIBO.setGifImage(gif, manager: gifManager)
gifIBO.contentMode = .center
guard monster.legendary! != true else {
// Value requirements not met, do something
monsterStatusLegend()
return
}
guard monster.subLegend! != true else {
// Value requirements not met, do something
monsterStatusSub()
return
}
guard monster.isMega! != true else {
// Value requirements not met, do something
monsterStatusMega()
return
}
}

you display all data depending on monster but you never change the monster depending which indexPath you used.
add some code to populate the monster from indexPath
monster = getMonster(index.row)
or in your case
#IBAction func monsPreviousIBO(_ sender: Any) {
self.index = IndexPath(row: self.index.row - 1, section: self.index.section)
monster = mons[self.index.row]
displayDataForIndexPath()
}
or better in the displayDataForIndexPath() add this line:
func displayDataForIndexPath() {
if index.row == 0 {
self.monsPreviousIBO.removeFromSuperview()
}
monster = mons[self.index.row]
//....
NOTE some suggestions:
i would change the line for the button then it is enabled if the indexpath gets >0:
self.monsPreviousIBO.isEnabled = (index.row != 0)
just save the row as monsterIndex = indexPath.row and then deal only with the index and not indexPath.
you don't need to save the current monster as monster if you use the monster only in displayDataForIndexPath - then you can get the current monster just there and have it a local variable in this function:
var monster = mons[self.index.row]

Related

How to find index of selected item within a UIStackView Swift 4

I have 5 buttons within a UIStackView, and I want to find out which index is being selected, and later compare those indexes. My code right now gives me an Array.Index. I've tried both subviews and arrangedSubviews. Is there anyway I can turn this into an Integer? I can't figure it out. Thanks!!
if let selectedIndex = stackview.subviews.index(of: sender) {
}
// UPDATE
I kinda got what I wanted with:
let int = stackview.subviews.distance(from: stackview.subviews.startIndex, to: selectedIndex)
I'm still not sure if this is the most efficient way, but it does the job for now.
index(of:) return Int.
Also you should find your button in the arrangedSubviews, not in the subviews
Assuming your stack view contains only buttons, and each button is connected to this #IBAction, this should work:
#IBAction func didTap(_ sender: Any) {
// make sure the sender is a button
guard let btn = sender as? UIButton else { return }
// make sure the button's superview is a stack view
guard let stack = btn.superview as? UIStackView else { return }
// get the array of arranged subviews
let theArray = stack.arrangedSubviews
get the "index" of the tapped button
if let idx = theArray.index(of: btn) {
print(idx)
} else {
print("Should never fail...")
}
}
I would add a tag to each of your buttons (button.tag = index) then check the tag of your sender.
So then you can wire up each of your buttons to the same function with a sender parameter, then check if sender.tag == index.

UITableViewController's Cell's Content disappearing

I am making a coin collection app which is supposed to help users maintain a portable record of their collection. There are two view controllers so far: the CoinTableViewController which presents a tableview of all the categories of coins, and the CoinCategoryViewController which is supposed to have a table view of the specific coins in this category.
I obviously want my coins to be reportable based on multiple criteria (such as the same year, same country, etc. and etc.). To this effect, I have created a reusable tableview cell subclass called CoinTableViewCell that has 5 UILabels which represent all the information that the coins into the collection can be grouped into categories by.
Here is what my storyboard looks like
The logic is that based on my current sorting criteria, I can hide certain labels from the cell to reflect the criteria that the coins were sorted by. The settings wheel opens up a menu that appears on the left side of the screen with options for how to sort the coins, and the closing of the menu makes the Coin Categories controller resort the coins in the collection if the sorting option was changed.
My problem is that while my program works overall, sometimes, some of the cells do not appear completely after the program resorts them (after the user opens up the menu and selects an option).
Here's what this looks like:
As you may see, the bottom two cells in the view controller are missing the top label even though the other cells have them. And since I have implemented the tableview controller's cells as being resizable, the table view cells should automatically resize themselves to fit the content inside.
Here is my code:
// Controls the table view controller showing the general coins (one per each category)
import UIKit
import CoreData
class CoinTableViewController: UITableViewController, NSFetchedResultsControllerDelegate, UISearchResultsUpdating, UITabBarControllerDelegate
{
//this is an array of all the coins in the collection
//each row of this two-dimensional array represents a new category
var coinsByCategory: [CoinCategoryMO] = []
var fetchResultController: NSFetchedResultsController<CoinCategoryMO>!
//we sort the coins by the category and then display them in the view controller
//example includes [ [Iraq Dinar 1943, Iraq Dinar 1200], etc. etc.]
//<OTHER VARIABLES HERE>
//the data here is used for resorting the coins into their respective categories
//the default sorting criteria is sorting the coins into categories with the same country, value, and currency
//and the user can change the app's sorting criteria by opening the ConfiguringPopoverViewController and changing the sorting criteria there
private var isCurrentlyResortingCoinsIntoNewCategories : Bool = false
override func viewDidLoad()
{
super.viewDidLoad()
self.tabBarController?.delegate = self
//we now fetch the data
let fetchRequest : NSFetchRequest<CoinCategoryMO> = CoinCategoryMO.fetchRequest()
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
let context = appDelegate.persistentContainer.viewContext
let sortDescriptor = NSSortDescriptor(key: "index", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do
{
try fetchResultController.performFetch()
if let fetchedObjects = fetchResultController.fetchedObjects
{
self.coinsByCategory = fetchedObjects
}
}
catch
{
print(error)
}
}
//if there is an empty area in the table view, instead of showing
//empty cells, we show a blank area
self.tableView.tableFooterView = UIView()
//we configure the row heights for the table view so that the cells are resizable.
//ALSO: should the user want to adjust the text size in "General"->"Accessibility"
//the text size in the app will be automatically adjusted for him...
tableView.estimatedRowHeight = 120
tableView.rowHeight = UITableViewAutomaticDimension
//WE CONFIGURE THE SEARCH BAR AND NAVIGATION BAR....
//if the user scrolls up, he sees a white background, not a grey one
tableView.backgroundView = UIView()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchResultController.sections!.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if searchController != nil && searchController.isActive
{
return searchResults.count
}
else
{
if let sections = fetchResultController?.sections
{
return sections[section].numberOfObjects
}
else
{
return 0
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//configure the cell
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CoinTableViewCell
//Initialize the Cell
let category = (searchController != nil && searchController.isActive) ? searchResults[indexPath.row] : coinsByCategory[indexPath.row]
//we now remove the extra labels that we do not need
cell.configureLabelsForCategoryType(theType: (category.coinCategory?.currentCategoryType)!)
let sampleCoin : Coin = category.coinCategory!.getCoin(at: 0)!
cell.countryLabel.text = "Country: \(sampleCoin.getCountry())"
cell.valueAndDenominationLabel.text = "Value & Denom.: \(sampleCoin.valueAndDenomination)"
//now we add in the quantity
cell.quantityLabel.text = "Quantity: \(String(describing: coinsByCategory[indexPath.row].coinCategory!.count))"
//we now add in the denomination
cell.denominationOnlyLabel.text = "Denom.: \(sampleCoin.getDenomination())"
//we now add in the year
if sampleCoin.getYear() == nil
{
cell.yearLabel.text = "Year: " + (Coin.DEFAULT_YEAR as String)
}
else
{
let yearABS = abs(Int32(sampleCoin.getYear()!))
cell.yearLabel.text = "Year: \(yearABS) \(sampleCoin.getYear()!.intValue > 0 ? TimePeriods.CE.rawValue : TimePeriods.BCE.rawValue)"
}
//we add in an accessory to indicate that clicking this cell will result in more information
cell.accessoryType = .disclosureIndicator
return cell
}
func deleteCoinCategory(rowPath: IndexPath)
{
if 0 <= rowPath.row && rowPath.row < self.coinsByCategory.count
{
//we have just tested that the rowPath index is valid
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
let context = appDelegate.persistentContainer.viewContext
let coinCategoryToDelete = self.fetchResultController.object(at: rowPath)
context.delete(coinCategoryToDelete)
appDelegate.saveContext()
//ok we now deleted the category, now we update the indices
updateIndices()
appDelegate.saveContext()
}
}
}
func deleteCoin(c: Coin, indexOfSelectedCategory: IndexPath) -> Bool
{
//we have a coin that we want to delete from this viewcontroller
//and the data contained in it.
//
//the parameter indexOfSelectedCategory refers to the IndexPath of the
//row in the TableView contained in THIS viewcontroller whose category
//of coins we are modifying in this method
//
//Return value: a boolean that indicates whether a single coin has
//been deleted - meaning that the user should return to the parentviewcontroller
if 0 < indexOfSelectedCategory.row && indexOfSelectedCategory.row < self.coinsByCategory.count && self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.hasCoin(c: c) == true
{
//the index is valid as it refers to a category in the coinsByCategory array
//and the examined category has the coin in question
if self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.count == 1
{
//the coin "c" that we are going to delete is the only coin in the entire category
//we reduce the problem to a simpler one that has been already solved (thanks mathematicians!)
self.deleteCoinCategory(rowPath: indexOfSelectedCategory)
return true
}
else
{
//there is more than one coin in the category
self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.removeCoin(c: c)
//we save the changes in the database...
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
appDelegate.saveContext()
}
return false
}
}
return false
}
func addCoin(coinToAdd: Coin)
{
//we check over each category to see if the coin can be added
var addedToExistingCategory: Bool = false
if let appDelegate = UIApplication.shared.delegate as? AppDelegate
{
for i in 0..<self.coinsByCategory.count
{
if self.coinsByCategory[i].coinCategory?.coinFitsCategory(aCoin: coinToAdd) == true
{
//we can add the coin to the category
self.coinsByCategory[i].coinCategory = CoinCategory(coins: self.coinsByCategory[i].coinCategory!.coinsInCategory+[coinToAdd], categoryType: coinsByCategory[i].coinCategory!.currentCategoryType)
addedToExistingCategory = true
break
}
}
if addedToExistingCategory == false
{
//since the coinToAdd does not fall in the existing categories, we create a new one
let newCategory = CoinCategoryMO(context: appDelegate.persistentContainer.viewContext)
newCategory.coinCategory = CoinCategory(coins: [coinToAdd], categoryType: CoinCategory.CategoryTypes.getTheCategoryFromString(str: UserDefaults.standard.object(forKey: "currentSortingCriteria") as! NSString).rawValue)
//this index indicates that we are going to insert this newCategory into index "0" of all the categories in the table
newCategory.index = 0
}
appDelegate.saveContext()
//now since we have added the coin, we now updated the indices of each CoinCategoryMO object
updateIndices()
}
}
func coinFitsExistingCategory(coin: Coin) -> Bool
{
//this function checks if the coin can be added to the existing categories
for i in 0..<self.coinsByCategory.count
{
if self.coinsByCategory[i].coinCategory?.coinFitsCategory(aCoin: coin) == true
{
//we can add the coin to the category
return true
}
}
return false
}
func resortCoinsInNewCategories(newCategorySetting : CoinCategory.CategoryTypes?)
{
//we want to resort all the coins in the category by new sorting criteria
if newCategorySetting != nil && newCategorySetting! != CoinCategory.CategoryTypes.getTheCategoryFromString(str: UserDefaults.standard.object(forKey: "currentSortingCriteria") as! NSString)
{
//We have a valid CoinCategory.CategoryTypes sorting criteria that is different from the one currently used.
//We resort the coins in the collection by the new category
UserDefaults.standard.setValue(newCategorySetting!.rawValue, forKey: "currentSortingCriteria")
if self.coinsByCategory.count != 0
{
//we actually have some coins to resort... let's get to work!
self.isCurrentlyResortingCoinsIntoNewCategories = true
//we first get an array of all the coins in existing categories
var allCoinsArray : [Coin] = []
for i in 0..<self.coinsByCategory.count
{
allCoinsArray += self.coinsByCategory[i].coinCategory!.coinsInCategory
}
//now we need to delete all the categories in existence...
let firstCategoryIndexPath = IndexPath(row: 0, section: 0)
let numberOfCategoriesToDelete = self.coinsByCategory.count
for _ in 0..<numberOfCategoriesToDelete
{
self.deleteCoinCategory(rowPath: firstCategoryIndexPath)
}
//OK... now that we have deleted all old categories... it is time to start to create new ones...
for i in 0..<allCoinsArray.count
{
//AND we add the coin to the array!
//this function also automatically updates the indices, so it is not an issue there
self.addCoin(coinToAdd: allCoinsArray[i])
}
//we are done resorting
self.isCurrentlyResortingCoinsIntoNewCategories = false
}
}
}
private func updateIndices()
{
//this function updates the "index" property so that
//each CoinCategoryMO object in the coinsByCategory array
//has an index corresponding to its position.
//After this function is called, we must save the core data in the AppDelegate.
//
//This function is called ONLY after the changes to the CoinCategoryMO objects
//are saved in core data and the self.coinsByCategory array is updated to have
//the latest version of the data
for i in 0..<self.coinsByCategory.count
{
//the only reason why we create an entirely new CoinCategory object
//is that the creation of an entirely new CoinCategory object
//is the only way that the appDelegate will save the information
self.coinsByCategory[i].index = Int16(i)
}
if let appDelegate = UIApplication.shared.delegate as? AppDelegate
{
appDelegate.saveContext()
}
}
//these delegate methods control the core data database
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
{
switch type
{
case .insert :
if let newIndexPath = newIndexPath
{
tableView.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath
{
tableView.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath
{
tableView.reloadRows(at: [indexPath], with: .fade)
}
default:
tableView.reloadData()
}
if let fetchedObjects = controller.fetchedObjects
{
self.coinsByCategory = fetchedObjects as! [CoinCategoryMO]
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
tableView.endUpdates()
if self.isCurrentlyResortingCoinsIntoNewCategories != true
{
//we let the user know if the collection is empty
if self.coinsByCategory.count == 0
{
self.messageUserIfCollectionEmpty()
}
else
{
self.activateCollectionEmptyLabel(newState: false)
}
}
}
And then my CoinTableViewCell class is:
// Represents a cell of the coin buttons
import UIKit
class CoinTableViewCell: UITableViewCell {
#IBOutlet var countryLabel: UILabel!
#IBOutlet var valueAndDenominationLabel: UILabel!
#IBOutlet var quantityLabel: UILabel!
#IBOutlet var denominationOnlyLabel : UILabel!
#IBOutlet var yearLabel : UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func restoreAllLabelsToCell()
{
//this is a function that is called when this cell is being initialized in the cellForRowAt method in a tableview..
//we want to make all the labels visible so that the previous usage of a reusable tableview cell does not affect this usage of the cell
countryLabel.isHidden = false
valueAndDenominationLabel.isHidden = false
quantityLabel.isHidden = false
denominationOnlyLabel.isHidden = false
yearLabel.isHidden = false
}
func configureLabelsForCategoryType(theType : NSString)
{
//in this function, we remove all the extra labels
//that contain information that does not relate to the general type of the category from the stack view
//For example, the year label is removed when the category is a country, as a year does not determine what category a coin falls into.
//we restore all the labels in this cell as we do not want the reusable cell's past usage
//which may have lead to a label dissappearing to carry over into this new usage of the cell
self.restoreAllLabelsToCell()
switch theType
{
case CoinCategory.CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue:
//we do not need information about the coin's denomination (without its value) or the year
denominationOnlyLabel.isHidden = true
yearLabel.isHidden = true
//after we remove the labels, we now make the first label bold and black
valueAndDenominationLabel.font = UIFont.boldSystemFont(ofSize: self.valueAndDenominationLabel.font.pointSize)
valueAndDenominationLabel.textColor = UIColor.black
case CoinCategory.CategoryTypes.COUNTRY.rawValue:
//we do not need the information about the coin's value and denominations nor year
valueAndDenominationLabel.isHidden = true
denominationOnlyLabel.isHidden = true
yearLabel.isHidden = true
//after we remove the labels, we make the first label bold and black
countryLabel.font = UIFont.boldSystemFont(ofSize: self.countryLabel.font.pointSize)
countryLabel.textColor = UIColor.black
case CoinCategory.CategoryTypes.CURRENCY.rawValue:
//we do not information about the coin's value & denomination (together, that is), or year
valueAndDenominationLabel.isHidden = true
yearLabel.isHidden = true
//after we remove the labels, we make the first label bold and black
denominationOnlyLabel.font = UIFont.boldSystemFont(ofSize: self.denominationOnlyLabel.font.pointSize)
denominationOnlyLabel.textColor = UIColor.black
case CoinCategory.CategoryTypes.YEAR.rawValue:
//we do not information about the coin's value, denomination, or country
valueAndDenominationLabel.removeFromSuperview()
denominationOnlyLabel.isHidden = true
countryLabel.isHidden = true
//after we remove the labels, we make the first label bold and black
yearLabel.font = UIFont.boldSystemFont(ofSize: self.yearLabel.font.pointSize)
yearLabel.textColor = UIColor.black
default:
//the string does not match any of the categories available
//we do not remove any labels
break
}
}
}
My CoreData implementation is a collection of CoinCategoryMO objects which have the property "Index" (for their position in the uitableviewcontroller) and a CoinCategory object which holds objects of the Coin Class.
I have been trying to debug this for several days now, and I have no idea what is going wrong. Could anyone please help?
Many many thanks in advance, and have a great day!
I guess, CategoryType in category.coinCategory?.currentCategoryType is not same across all objects in searchResults / coinsByCategory.
i.e. 2nd and 3rd objects has COUNTRY_VALUE_AND_CURRENCY as its currentCategoryType and 4th and 5th has COUNTRY.
Can you confirm that currentCategoryType is same for all objects in searchResults / coinsByCategory?
I think I know what the error is now:
In my configureLabelsForCategoryType method for my CoinTableViewCell class, I had invoked the removeFromSuperView() method on the valueAndDenomination label, which had the effect of permanently removing that label from the cell.
Thus the cell did not have that label, even if it was later reused for another CoinCategory.
I replaced the valueAndDenominationLabel.removeFromSuperView() line with valueAndDenominationLabel.isHidden = true which has the effect of hiding the valueAndDenominationLabel from the user, but not permanently removing it from the cell.
Many many thanks to those who took the time to read my question and to respond.
Your efforts helped me think about my problem and track it down!
Thank you!

Reloading the collectionView after delete

I'm using firebase. My code works but my issue is when I press the accept button, I want that index to be deleted from the collectionView and I want the table to reload right away right after. I can't use the slide to delete because I need it for another function.
This is in my cell class thats why I did not use self. to call the collectionView. This function is being called by a addTarget() method. I'm thinking maybe passing an indexPath as a parameter, but I don't know how to pass an indexPath.item as a parameter and also how to pass a parameter inside an addTarget() method.
func handleAccept(_ sender: UIButton) {
print("button pressed")
guard let artId = art?.artId else {return}
let approvedRef =
FIRDatabase.database().reference().child("approved_art")
approvedRef.updateChildValues([artId: 1])
let pendingRef =
FIRDatabase.database().reference().child("pending_art").child("\(artId)")
pendingRef.removeValue { (error, ref) in
if error != nil {
return
}
let layout = UICollectionViewFlowLayout()
let pendingArtCollectionViewController =
PendingArtsCollectionViewController(collectionViewLayout: layout)
DispatchQueue.main.async {
pendingArtCollectionViewController.collectionView?.reloadData()
}
}
}
You don't need to pass indexpath as argument, you can get the indexpath of the tableView in button action with the following code :
func handleAccept(_ sender: UIButton) {
let center = sender.center
let rootViewPoint = sender.superview!!.convert(center!, to: self.myTableView)
let currentIndexpath = myTableView.indexPathForRow(at: rootViewPoint)
}

How to get the indexPath of the current cell

I have currently trying to create a way to delete an item in a collection view at the indexPath of 1 back. So far i have used some help to create a function with scrollview did scroll to create a way to count which image the user is on by the current image method. I now need a way to count which cell the user is on. Here is my code>
var currentImage = 0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let x = floor(myCollectionView.contentOffset.x / view.frame.width)
if Int(x) != currentImage {
currentImage = Int(x)
//print(currentImage)
}
if currentImage > 0 {
for collectionCell in myCollectionView.visibleCells as [UICollectionViewCell] {
let indexPath = myCollectionView.indexPath(for: collectionCell as UICollectionViewCell)!
let indexPathOfLastItem = (indexPath?.item)! - 1
let indexPathOfItemToDelete = IndexPath(item: (indexPathOfLastItem), section: 0)
imageArray.remove(at: 0)
myCollectionView.deleteItems(at: [indexPathOfItemToDelete])
currentImage = 1
Based more on the comments than your actual question, what you seem to want is to get the first visible cell's index path so you can use that path to delete the cell.
let visibleCells = myCollectionView.visibleCells
if let firstCell = visibleCells.first() {
if let indexPath = myCollectionView.indexPath(for: collectionCell as UICollectionViewCell) {
// use indexPath to delete the cell
}
}
None of this should be used or done in the scrollViewDidScroll delegate method.
[Updated for Swift 5.2] Here's a slightly more succinct way than #rmaddy's answer (but that one works just fine):
guard let indexPath = collectionView.indexPathsForVisibleItems.first else {
return
}
// Do whatever with the index path here.

how can I refresh a single button in UITableViewCell instead of refreshing whole table or whole cell?

In my swift app I have a UITableView with one static cell and many dynamic cells.
Static cell contains different fields, such as labels, map (taken from MapKit) and a button, that indicates whether user voted up or not.
Now, when user presses the button, I want to change its color, possibly without refreshing anything else.
So far my code looks like this:
var currentUserVote:Int = 0
func tableView(_ tview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath as NSIndexPath).row == 0 {
let cell = tview.dequeueReusableCell(withIdentifier: "cellStatic") as! VideoDetailsCell
fetchScore(cell.score)
let voteUpImage = UIImage(named: "voteUp");
let tintedVoteUpImage = voteUpImage?.withRenderingMode(UIImageRenderingMode.alwaysTemplate)
cell.voteUpButton.setImage(tintedVoteUpImage, for: UIControlState())
checkUsersVote() { responseObject in
if(responseObject == 1) {
cell.voteUpButton.tintColor = orangeColor
} else if (responseObject == -1){
cell.voteUpButton.tintColor = greyColor
} else {
cell.voteUpButton.tintColor = greyColor
}
self.currentUserVote = responseObject
}
//map handling:
let regionRadius: CLLocationDistance = 1000
let initialLocation = CLLocation(latitude: latitude, longitude: longitude)
centerMapOnLocation(initialLocation, map: cell.mapView, radius: regionRadius)
//cell.mapView.isScrollEnabled = false
cell.mapView.delegate = self
.
.
.
return cell
} else {
//handle dynamic cells
}
}
So in the method above I'm checking if user voted already and based on that I'm setting different color on the button. I'm also centering the map on a specific point.
Now, since it's a static cell, I connected IBAction outlet to that button:
#IBAction func voteUpButtonAction(_ sender: AnyObject) {
if(currentUserVote == 1) {
self.vote(0)
}else if (currentUserVote == -1){
self.vote(1)
} else {
self.vote(1)
}
}
and the vote method works as follows:
func vote(_ vote: Int){
let indexPath = IndexPath(row: 0, section: 0)
let cell = tview.dequeueReusableCell(withIdentifier: "cellStatic") as! VideoDetailsCell
switch(vote) {
case 1:
cell.voteUpButton.tintColor = orangeColor
case 0:
cell.voteUpButton.tintColor = greyColor
case -1:
cell.voteUpButton.tintColor = greyColor
default:
cell.voteUpButton.tintColor = greyColor
}
tview.beginUpdates()
tview.reloadRows(at: [indexPath], with: .automatic)
tview.endUpdates()
currentUserVote = vote
//sending vote to my backend
}
My problem is, that when user taps the button, he invokes the method vote, then - based on the vote, the button changes color, but immediately after that method cellForRow is called and it changes the color of the button again. Also, it refreshes the map that's inside of it.
What I want to achieve is that when user taps the button, it should immediately change its color and that's it. Map stays untouched and the button is not changed again from cellForRow.
Is there a way of refreshing only that particular button without calling again cellForRow?
First of all, you confuse static and dynamic cells. You can use static cells only in the UITableViewController and you can't use static and dynamic cell at the same time.
I strongly recommend you not to use cell for storing map and button. All elements from the cell will be released after scrolling it beyond the screen.
I can advise you use TableViewHeaderView for this task. In this case you will be able set button and map view as #IBOutlet.
(See more about adding tableview headerView. You can also set it from interface builder.)
Another way is change tableView.contentInset and set your view with map and button as subview to tableView. This method is used when you need create Stretchy Headers.
It should be quite easy, simply do it in your button handler. The sender argument there is the button object that caused the action. When you were connecting it from IB there was a dropdown to select sender type, you may have missed it and the whole thing would have been obvious with UIButton type there.
Simply change your handler like this :
#IBAction func voteUpButtonAction(_ sender: UIButton) {
if(currentUserVote == 1) {
self.vote(0)
}else if (currentUserVote == -1){
self.vote(1)
} else {
self.vote(1)
}
sender.backgroundColor = yourFavouriteColor
}
Another approach would be to create an IBOutlet for your button, since its from a static cell, and then you would be able to reference it from any place in your view controller.
In this call:
func tableView(_ tview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
I see it calls checkUsersVote() which I'm guessing should get the updated value set in the vote() call. Could the problem be that you aren't doing this
currentUserVote = vote
until after reloadRows() is called?

Resources