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

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.

Related

Recieve data when tableView is loading

I am making a simple like button. When a person presses the like button, the code is checking if the user already pressed like before. If not, it sends data to Firebase saying the user is liking this post, and it is then changing the textColor of the button to blue.
My problem is, when the user closes the app and opens it again, none of the buttons are blue. Even though the user liked several posts. I need some kind of code that can check if the user already liked the post and change the textColor to blue when the UITableViewController is loading, I guess?
I tried to do that in my code, but it gets an error. So can anyone tell me how to go from here? My like button code:
func checkClickOnLikeButton() {
let dataPathen = self.pathDB
// print(dataPathen)
if let user = FIRAuth.auth()?.currentUser {
let userId = user.uid
FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user value
self.gottenUserId = snapshot.value![userId] as? Bool
// print(self.gottenUserId)
if self.gottenUserId == true {
print("Der er trykket high five før")
} else {
print("Der er IKKE trykket like før")
let quoteString = [userId: true]
FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost").updateChildValues(quoteString)
let blueButtonColor = UIColor(red: 0/255, green: 122/255, blue: 255/255, alpha: 1.0)
self.highFiveButton.setTitleColor(blueButtonColor, forState: UIControlState.Normal)
}
// ...
}) { (error) in
print(error.localizedDescription)
}
}
}
Id create a struct that fits your data. I would then keep the data you pull from the database locally in an array.
While building the table i.e cellForRowAtIndexPath, check indexPath.row against the value in your array to see if you've liked it.
If you have liked it, i.e arrayOfUsers[indexPath.row].contains("myUserID"), then adjust your button to show already liked, and assign a number to the counter. Else, Do nothing.
Also - It wouldn't hurt to keep the query updated rather than doing observeSingleEventOfType(...), The reason I say this is you can then tell the query to persist data so even if the user closes app. launches your app on an iPad, and then goes back to the phone, the iPhone will be updated thanks to firebase.
So, to summarize that last bit, I would recommend changing the query not to observer once, and have it call specific functions when a child is removed / added

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.

Voting buttons with Parse in Swift

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.

Display a particular user and all their information stored on parse on a different View Controller in Swift

In my app each user sets up a profile page and all this info is stored on Parse. When the current user hits a button it segues to a tableViewController that displays in cells all the other users on the app. In each cell there is a button which the user presses to view the full profile page of the user that they have selected. I have a VC set up to hold all the info I just don't know how to display the particular user that the current user has selected into it. How do I single out the chosen users information? I have read the Parse documentation on relational queries but I can't seem to get my head around it. https://parse.com/docs/ios/guide#queries-relational-queries
So in finer detail basically all my users are displaying on a VC. It has name and profile picture, when the user clicks on the info button beside a certain user it should open a new screen and display all of this users information stored on Parse.
I have tried creating a static outside the class
struct Data { var OtherName:String! var id:String! }
And then in my ViewDidLoad:
let query = PFQuery(className: "_User")
// otherQuery.orderByDescending("createdAt")
query.whereKey("username", notEqualTo:PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock { (users: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// success
print(users!.count)
for user in users! {
self.imageFiles.append(user["image"] as! PFFile)
self.instrumentText.append(user["instrument"] as! String)
self.nameText.append(user["name"] as! String)
self.ageText.append(user["age"] as! String)
// self.locationText.append(user["location"] as! PFGeoPoint)
var singleData = Data()
singleData.id = user.objectId
singleData.OtherName = user["name"] as! String
print("\(singleData)")
} // users
Then on the VC that the info button segues to I am trying to call it and upload each label with just the user that was selected.
self.userName.text = "" as String
But the "" is staying blank.
Then I was thinking maybe if I add that when the more info button is pressed that the user it was pressed on gets chosen and is added to a chosen string on Parse and then I just have to call the user from the chosen string when I am on the chosen users profile page and call the info that way and then when the user clicks the back button the user is no longer chosen. It just seems like I would be adding and taking away a lot of users to "chosen" in Parse is there a better way?
I am able to display all the users info but how do I just display a chosen user from Parse without using the objectId because at the top of each cell it will return a different user here each time depending on settings previously set on a different VC such as only show users in a certain age bracket etc. I know I must set an Id of some sort on button touched but I am at a complete loss. The only online help out there that I can find is set for other languages eg. https://parse.com/docs/js/guide#users-querying>
So I guess my question is :
1.Does anyone know a tutorial they could point me towards.
2.Does anyone know how I even get started and I can go from there myself.
3.Can anyone help?
*Swift and Parse newbie little to no experience in other languages.
New code in firstVC:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "userProfileDetailsSegue" {
if let indexPath = resultsPageTableView.indexPathForSelectedRow {
let controller = (segue.destinationViewController) as! UserProfileDetailsViewController
***error here: UserProfileDetailsViewController.userToShowDetail = indexPath.row
//line above is the one I can't get to work it needs to equal to the cell selected?
}
}
}
New Code in destinationVC:
var userToShowDetail: PFUser? {
didSet {
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let userToShowDetail: PFUser = self.userToShowDetail {
if let label = self.userName {
nameText.append(userToShowDetail["name"] as! String)
// self.nameText.append(user["name"] as! String)
}
}
}
You're going to need to do a couple of things for this to work. First, in the view controller that you're using to show another user's information, you need to set a property like var userToShowDetail: PFUser?. Then in your view with all of the users of your app, you need to create a segue to bring them from the current view to the detail view. Assuming you're using a TableView, you can connect just one prototype cell to the detail view you're trying to navigate. Then you'll need to override prepareForSegue and set the new view controller's user equal to the user at the row that was selected. I don't know how you're storing your objects of user's of your app, but you should probably be able to do something like this.
override fun prepareForSegue(segue: UIStoryboardSegue) {
if segue.identifer == "segueToShowDetailInfo" {
//get the index path for the row that was selected
let indexPath = self.tableView.indexPathForSelectedRow()
//get the PFUser object for that particular row
let userToShow = self.appUsers[indexPath.row]
//create your new view controller
let newVc = segue.destinationViewController as! DestinationVC
//assign the new vc's property to your object
newVc.userToShowDetail = userToShow
}
}
But in your query, you probably shouldn't have a separate array of the the different parts of the user's data. You should just make one array, call it var appUsers = [PFUser]() and then when you get a user back in your query, just say:
for user in users! {
self.appUsers.append(user) as! PFUser
}
There, now you have a whole array of objects that holds a reference to every user. When someone is using your app, and selects a cell, then you can perform the prepare for segue method as above.
Note that I also noticed you have a didSet on your variable in your new view controller. Calling a function to update the UI after this variable is set will not work, and may crash, because the view controller doesn't have a window yet.

A UIButton is pressed in 1 VC and I need it to save the buttons title to a label in a different view controller not directly segued to each other

In the initial set up of my app I need the user to chose a genre type. i have it set up such that the user selects from a list of UIButtons which are all different genre types. I need this genre selection to save to the users profile. The next view controller is the same set up as the genreVC but asking for instrument instead of genre. The next segue from here is to view controller 3 the usersVC profile and it should have saved which two buttons were selected on the previous two VC's by updating a label. I have tagged each button with a number and have tried next to everything.
How do I save which button has been pressed on a different view controller to the users profile and update the label on the users profile to the name of the button pressed?
I am using each button individually and have them tagged as such as seen here:
#IBAction func popGenreButton(sender: UIButton) {
if(sender.tag == 6){
print("Pop genre selected")
}
}
#IBAction func altGenreButton(sender: UIButton) {
if(sender.tag == 7){
print("Alternative genre selected")
}
}
#IBAction func electronicGenreButton(sender: UIButton) {
if(sender.tag == 8){
print("Electronic genre selected")
}
}
I also tried adding each button into genreSelection as shown below that didn't work so I stuck with the individual buttons above.
#IBAction func genreSelectionButton(sender: UIButton) {
print(sender.titleLabel!.text!)
genreResult.text = genreString + " genre has been saved to your profile"
}
I also created a nib with myModalDelegate and protocol and nope.
I thought maybe I could create a genre array and when a certain button is selected it calls a certain item from the array and updates that item to the label but I was unsure how to go about this too.
I am using Xcode 7 and Swift 2.0 and Parse as my cloud server.
As you can see I have a lot to learn yet with Swift. I have been searching how to do this for the past week and can't figure it out. I am a 'lot' out of my depth but want to figure it out, if anyone can help that would be great!
A simple solution would be something like creating a global store for those values
i.e.
struct Choices {
static var genre: String?
static var instruments: String?
}
then you could in your different view controllers write and read from it by simply
Choices.instrument = "trumpet"
or
label.text = Choices.genre
A prettier solution would be to create a similar class as the previous struct but not have the variables static. You could then pass it along and fill up with the values from each VC and use the values in the last VC where you are setting the label text

Resources