Voting buttons with Parse in Swift - ios

A quick overview of what I'm trying to make. I'm making an app to show my film reviews. I have a PFQueryTableviewController of which I use a custom cell. This table shows films which come from my Parse database. In each cell, there a 3 UIButtons, of which I want the user to use to vote for the film (happyVote, OkVote, sadVote). Over the top of each button is a UILabel that simply displays a count.
How I want it to work
When a user presses one of the buttons, the vote increases by 1.
When a user presses the same button again, the vote decreases by 1. Or,
If the user had pressed a different button, the vote decreases on the first button and increases on the button just pressed.
The user can only ever vote on one of the buttons.
The vote is shown by the UILabel showing the count, and by the button image changing.
See below for a visual:
This is what I've added in Parse:
So far, I've added the code to increase the vote count in Parse, in my TableViewController.swift:
#IBAction func upVoteButton(sender: AnyObject) {
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = objectAtIndexPath(hitIndex)
object!.incrementKey("UpVoteCount")
object!.saveInBackground()
self.tableView.reloadData()
}
This kind of works, except, the user can keep increasing the count, and they can vote on all 3 buttons. And it doesn't change the Button Image when pressed.
In my cellForRowAtIndexPath method, I've put:
let downVote = object!.valueForKey("DownVoteCount") as! Int
cell.downVoteLabel.text = "\(downVote)"
let middleVote = object!.valueForKey("MiddleVoteCount") as! Int
cell.middleVoteLabel.text = "\(middleVote)"
let upVote = object!.valueForKey("UpVoteCount") as! Int
cell.upVoteLabel.text = "\(upVote)"
I have searched for a while for some examples of how to figure the rest out, but can't find anything, and I'm really struggling to figure the next steps out.
Any help will be greatly appreciated, and please let me know if you need/want to see anymore of my code. Thanks

In your upVoteButton() method, you can add the other two buttons and set them button.enabled = false inside an if statement like:
if downVoteButton.enabled == true {
downVoteButton.enabled = false
} else if downVoteButton.enabled == false {
downVoteButton.enabled = True
}
And do the same with the middleVoteButton in antoher if statement or the same, whatever floats your boat, also within the other button methods with the appropriate buttons. This helps disables the buttons when pressed and then enables it when it's pressed again.
And for the image changing you can follow this.

Related

Detecting which ImageView has has been tapped

So i have 4 vStacks, each containing 9 ImageViews. Each ImageView represents one Card, alpha 0 is default. When a Card is Detected (with ARKit), my code sets the ImageView to alpha 1 so the user can see that the card has been scanned.
Now: I want to implement that when the user clicks on one of the ImageViews, an alert should pop up asking the user if he is sure he wants to delete the scanned card. My problem is, I have no idea what the best practice is to get the information that the card has been tapped and how to delete it without hardcoding.
In ViewDidLoad i set the images into the ImageVies like This:
//This repeats for all 36 ImageViews
imgView1.image = UIImage(named: "Eichel_6")
imgView2.image = UIImage(named: "Eichel_7")
/*When a image is detected with ARKit, this is what happens. Basically
*it pushes the corresponding reference name to an array called
* scannedCards, handles them, and removes them afterwards.
* spielPoints = gamepoints/points, spielmodus = gamemode
*/
func updateLabel() {
//print all cards in scanned cards
for card in scannedCards {
points += (DataController.shared.spielpoints[DataController.shared.spielmodus!]![card]! * DataController.shared.multCalculator[DataController.shared.spielmodus!]!)
}
scannedCards.removeAll()
}
I am a new to coding, I would be grateful if you correct me if my code snippets are bad, beside my question. Thank you in advance.
As has already been mentioned in comments, you should use a UICollectionView for this kind of work. #Fogmeister has promised to add an answer concerning that later, so I won't do that. But I can answer the actual question, even though it's not what you should do.
From your code I can see that you probably have outlets for all your imageViews (imgView1 ... imgView36) and set each image manually. To detect taps on any of these, you could do something like this:
func viewDidLoad(){
super.viewDidLoad()
let allImageViews = [imageView1, imageView2, .... imageView36]
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapImageView(gesture:)))
allImageViews.forEach({$0.addGestureRecognizer(tapGestureRecognizer)})
}
#objc func didTapImageView(gesture:UITapGestureRecognizer){
guard let imageView = gesture.view as? UIImageView else { return }
//Here you can put code that will happen regardless of which imageView was tapped.
imageView.alpha = 0.0
//If you need to know exactly which imageView was tapped, you can just check
if imageView == self.imageView1{
//Do stuff only for imageView1
}else if imageView == self.imageView2{
//...
}//....
}
Again, this is not very good practice. If you go for UICollectionView instead, you don't have to have outlets for all your imageViews and you don't have to create a gestureRecognizer for handling events. But still, I hope this helped you understand general gestures better.

How to allow only one UIImageView to be touched at a time

I have built a simple memory game which consists of 16 UIImageViews.
For the memory game, you are only supposed to be able to touch one card at a time. When you touch the card, it flips over and reveals the image underneath
My issue is that I only want the user to be able to touch one image view at a time. When a user taps on two cards at the same time, it glitches the game. So my question is how do I allow only 1 UIImageView to perform an action at a time, and not allow multiple UIImageViews to perform an action at the same time if touched simultaneously.
Assuming that the user has to unflip a card by selecting it again, I would suggest adding a property to your viewController to keep track of the selectedCard. If there is already a selected card, ignore all others.
var selectedCard: UIImageView?
#objc func handleTap(_ recognizer: UITapGestureRecognizer) {
guard let imageView = recognizer.view as? UIImageView else { return }
if let selected = selectedCard {
if selected === imageView {
// unflip the card
imageView.image = backOfCard
selectedCard = nil
}
} else {
// display face of card
imageView.image = ...
selectedCard = imageView
}
}
Note: Even if a user taps two cards at the same time, handleTap will be called twice in succession. The first card will win and be flipped and become the selectedCard and the second card will be ignored because the if selected === imageView test will fail.
You have a number of choices.
You could put a single tap gesture recognizer on the superview that holds all of your card views and add logic to figure out which one was tapped. Since there is a single method to identify taps, you would't ever get more than one.
Alternately, you could add an instance variable to your class that keeps track of the state, and use it to set userInteractionEnabled to false on the image views that you don't want the user to tap.

Create a favorite button that connects to a favorite tableview in swift

I was wondering how to create a favorite button in swift that when that button is pressed the object that is "favorited" becomes part of a tableview and stays that way. I hear that core data is a way to do this, but I am still struggling to have the button change when pressed.
When I was creating a button all I get is a button that only changes the first time it presses. An example is I have an empty star button, which means that it is not a favorite, and when i pressed it it changes to a filled heart, which means that it is favorited. When I press the button a second time, when it is currently a filled star, it doesn't change and still remains a filled star.
The other problem that I am having is sending the information from the object to a favorite tableview. I am not sure how to keep something in a tableview as long as the favorite button is switched to filled star.
What I am seeing a lot of is this topic but it is all about making a favorite button in table views. I am not looking for tableviews. An example of what I am trying to do is like a favorite book app. The app opens as a tableview full of books, and when a cell is pressed it opens a new view controller with that book's information, and at the top is a favorite button that is normally an empty star. If I like that book I would like to press that button and the empty star becomes a filled star button, indicating that I like it. The book's information is then sent to a new table view that holds all of the liked books i have done before. If I no longer like that book i would like to press the filled star button again and it is removed from the favorite tableview list.
I am some experience with mysql in regards to swift, and I know that someone people have made comments about using some kind of persistence to save the state of the button.
My biggest problem is that I don't even know where to start. Every attempt I make seems to end the same way, so i don't really have any source code for others to see. I have looked online and through github but the closest thing I could find was a cocoapod named DoButton, but it hasn't been updated in a long time and I am worried that it won't last with another swift update. If someone could lead me in the right path or know a good tutorial, I would greatly appreciate it. If there are any questions I can answer i will answer them to the best of my ability.
Another Update: I managed to get the button to work. It connects to core data and saves the state of the button even when it is quit. Now all that is left is creating a favorite tableView to store the favorites.
Here is the code:
class ViewController: UIViewController {
var buttonIsSelected = false
var favorites = [Favorite]()
#IBOutlet var onOffButton: UIButton!
let image1 = UIImage(named: "empty") as UIImage?
let image2 = UIImage(named: "filled") as UIImage?
override func viewDidLoad() {
super.viewDidLoad()
print("buttonIsSelected: \(buttonIsSelected)")
let fetchedRequest: NSFetchRequest<Favorite> = Favorite.fetchRequest()
do {
let favorite = try PersistenceService.context.fetch(fetchedRequest)
for fav in favorite {
resetAllRecord(entity: fav.favorite)
buttonIsSelected = fav.favorite
print("fav.favorite: \(fav.favorite)")
print("button: \(buttonIsSelected)")
if fav.favorite == true {
onOffButton.setImage(image2, for: .normal)
}
}
} catch {
}
}
//button Action
#IBAction func onOffButtonPressed(_ sender: Any) {
buttonIsSelected = !buttonIsSelected
if buttonIsSelected == true {
onOffButton.setImage(image2, for: .normal)
} else if buttonIsSelected == false {
onOffButton.setImage(image1, for: .normal)
}
saveBool(bool: buttonIsSelected)
}
//save to core data
func saveBool(bool: Bool) {
if bool == true {
print("favorite")
print("buttonIsSelected \(buttonIsSelected)")
let liked = Favorite(context: PersistenceService.context)
liked.favorite = bool
PersistenceService.saveContext()
favorites.append(liked)
} else if bool == false {
print("unfavorite")
print("buttonIsSelected \(buttonIsSelected)")
let liked = Favorite(context: PersistenceService.context)
liked.favorite = bool
PersistenceService.saveContext()
favorites.append(liked)
}
}
//clears core data so it doens't get full
func resetAllRecord(entity: Bool) {
let context = PersistenceService.persistentContainer.viewContext
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Favorite")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do
{
try context.execute(deleteRequest)
try context.save()
}
catch
{
print ("There was an error")
}
}
}
In the object class create a bool parameter called isFavorite and default set it to false. When the favorite button is pressed the bool should change from false to true. When you have to access only the "favorite" objects, with a for loop iterate through your array of objects and through an if statement check if the isFavorite parameter is true. If it is, the element must be appended to a new object array maybe called favoriteObjects, and there you have all your favorite objects. The rest is pretty straightforward.

Not allowing touch interaction on certain views?

I've got a class where I handle my touches functions on buttons, and those buttons are also a Post class.
The logic I'm going for is "If the post.user is equal to the current firebaseuser, don't allow the user to move the button"
Here's what I tried:
In my "DragButtonsClass" I've got a notification being sent to my view controller on TouchesBegan.
In the view I've got
func touchesStarted(notif : NSNotification) {
if let dict = notif.userInfo! as? [NSObject: AnyObject] {
let tag = dict["tag"]!
let tagInt = Int(String((tag))) //this is probably super sloppy, don't know how else to change an AnyObject to an Int though.
let post = postArray[tagInt! - 1]
let postbutton = self.view.viewWithTag(tagInt!)
if post.user == currentUser {
postbutton?.userInteractionEnabled = false
print("Should be working")
} else {
print("Didn't work")
}
}
}
Basically I'm assigning tags to the buttons as they're created and those tags line up with my Posts array. I'm trying to check if the Post's user is the same as the current user by sending it over and then shut off user interaction.
I'm getting "Should be working" to print out but the button is still draggable.
This seems like a super sloppy and roundabout way to do this and most importantly it isn't working. I've read that turning off userInteraction is a way to stop touches from being recognized.
Any ideas?
Ended up just setting a bool "canBeDragged" in my button's class, and when the button is created the current user's ID is compared to the information being pulled from firebase and the bool is set. Then I just checked if the button could be dragged before my touchesMoved.

Swift 2: Call a method after pressing Return in UITextField

I have a Text Field in a Tip Calculator. I want the Tip Calculator to update all the fields of my calculator.
With the current code that I have now, for whatever reason. It doesn't call calcTip() (or atleast it doesn't seem to) until I press return once, enter another (or the same) value and press return again.
I'm really close to having this app work exactly how I want it to, and I feel like I'm just 1 or 2 lines away.
My ViewController conforms to UITextFieldDelegate, and I declared amountBeforeTaxTextField.delegate = self in viewDidLoad.
TextShouldReturn:
func textFieldShouldReturn(textField: UITextField) -> Bool {
amountBeforeTaxTextField.clearsOnBeginEditing = true
amountBeforeTaxTextField.becomeFirstResponder()
calcTip()
amountBeforeTaxTextField.resignFirstResponder()
return true
}
calcTip():
func calcTip() {
let tip = tipCalc.calculateTip()
let total = tipCalc.calculateTotal()
let tax = tipCalc.calculateTax()
let perPerson = total / Float(Int(partySizeSlider.value))
tipCalc.tipPercentage = (tipPercentageSlider.value)
tipCalc.amountBeforeTax = (amountBeforeTaxTextField.text! as
NSString).floatValue
tipCalc.taxPercentage = (taxPercentageSlider.value)
resultLabel.text = String(format: "Tip $%.2f, Tax: $%.2f Total: $%.2f", tip, tax, total)
perPersonLabel.text = String(format: "Per-Person Total: $%.2f", perPerson)
}
Note: I have the same outcome with & without .becomeFirstResponder()
Thanks for reading.
So your textFieldShouldReturn function is called when you press the bottom right button on the keyboard, which can have various titles; such as "Go", "Return", "Done", or "Send".
Your first line, only accomplishes a UI effect; meaning when the user taps on the textfield to input text, there will be a little box that appears on the far right handside of the textfield that allows the user to clear the inputted text, if there is any. Sort of like a reset.
When you use becomeFirstResponder, you are pretty much calling another keyboard to pop up, so you aren't accomplishing anything by doing that.
When you use resignFirstResponder, you are hiding the keyboard.
What you want to do is remove "becomeFirstResponder" as well as the first line, because those don't do anything to help your problem. That should be the solution to the problem you are facing.

Resources