evgenyneu / Cosmos

A star rating control for iOS/tvOS written in Swift
MIT License
2.19k stars 368 forks source link

Cosmos on UITableViewCell #17

Closed rupeshsaxena closed 8 years ago

rupeshsaxena commented 8 years ago

How can we use Cosmos Star on UITableViewCell , since my requirement is to get multiple rating stars to rate on specific cell index and send to server

evgenyneu commented 8 years ago

Hi, the CosmosView is simply a subclass of a UIView so you can use it in a table view cell and position with auto layout like any other view. I assume you know how to do it, if you don't I would suggest to learn that first. The demo app has "Performance" button that displays a screen with Cosmos rating controls in a table, so you can check how it is done there as an example.

table_view_cosmos

To collect rating from the user and send it to your server you can use didFinishTouchingCosmos property of cosmos view, there is example in the readme. You can assign a function to it after getting your cell view with dequeueReusableCellWithIdentifier function.

Feel free to experiment with user input by playing with the demo app. The cosmos view in the demo app has the user input switched off so it only displays rating but does not react to touch. You can switch user input on by setting Update on touch property to 'On' for the cosmos view in the storyboard.

rupeshsaxena commented 8 years ago

Thanks for your prompt response, I made an another closure for didFinishTouchingCosmos to get the specific cosmosview from the cell and get it's rating according to number of cells having CosmosView.

iosnewbie2016 commented 6 years ago

Hi, Can you pls help me with the closure you wrote, to get the cell associated with the rating view? Am able to get the TableView display the cosmos rating and didTouchCosmos/didFinishTouchingCosmos are getting triggered as well. It displays the ratings, but am not sure how to find the cell associated with it. Hope am clear.

evgenyneu commented 6 years ago

Hi @iosnewbie2016, good question. We can associate a UITableViewCell with the cosmos view touch event in the code that calls dequeueReusableCell, as shown in the demo app's PerformanceTableViewController:

https://github.com/evgenyneu/Cosmos/blob/40f22c99ce6ac48ef5a0b4365a13590cc2ad6007/Demo/PerformanceTableViewController.swift#L13

Here we get a cell object that is associated with the particular cell. We can then write a function or a property for the cell and assign it a unique identifier. When didFinishTouchingCosmos is called, we can use this identifier. This way we will know in which cell the user touched the stars. Will it work for you?

iosnewbie2016 commented 6 years ago

Hi @evgenyneu , I added one more property in settings of Cosmos and use that to identify the cell row number. Then return that along with ratung in didFinishTouchingCosmos.

I have created cell in the code like you have shown, but am not clear how you are suggesting to get a cell where the user touches the stars. I use a Viewcontroller with a Table View, instead of a Tableviewcontroller. I dont think that will make any difference.

evgenyneu commented 6 years ago

Yes, you can do this, but since you modified the library you will have to do this with each update of Cosmos. Alternatively, I think an easier approach would be to create a property in your UITableViewCell object. Since this object can contain an outlet for the Cosmos view, they are bundled together. Therefore, when your didFinishTouchingCosmos is called you can access your UITableViewCell object and read this property that you added to the cell. Am I explaining it right?

iosnewbie2016 commented 6 years ago

Hi @evgenyneu , Yes. I did create a class which subclasses UITableViewcell and it has a property to hold the rating view. let cell = tableView.dequeueReusableCell(withIdentifier: "MenuRequestCell", for: indexPath) as! MenuTableViewCell

and I can access it via cell.ratingView.

But in didFinishTouchingCosmos (or didTouchCosmos), I only get rating

func didFinishTouchingCosmos(_ rating: Double) {}

So, am not sure how do I get the cell corrosponding to this. Hope am clear.

Yes, I agree with you that it is not recommended to manipulate Cosmos, so that future upgrades are easier to handle.

evgenyneu commented 6 years ago

Sorry for not explaining it properly. Those Swift closures can be tricky to work with, but they allow to access the properties of the containing class through self. As you can see in the example below we can access the id property of the cell from the didFinishTouchingCosmos closure. Let me know if further clarification is needed.

public class PerformanceTableViewCell: UITableViewCell {
  @IBOutlet var cosmosView: CosmosView!
  var id: Int = 0

  func update(_ rating: Double, id: Int) {
    cosmosView.rating = rating
    self.id = id

    cosmosView.didFinishTouchingCosmos = { [weak self] rating in
      print(self?.id) // Access property here
    }
  }
}

A slight detail here is that we used weak self in the closure to avoid memory leaks.

iosnewbie2016 commented 6 years ago

Hi @evgenyneu , Thanks for the update. I was trying to use the didFinishTouchingCosmos in the main ViewController (and in the table view cell). I understand the disconnect now. I will try this and let you know if I run into any trouble. Thanks again.

manuG420 commented 4 years ago

Hi @evgenyneu I know this thread is super old already but in 2020 I am still struggling with this.

I am using a tableview and have multiple cosmos ratings within this tableview. The user can rate the different items in the tableview itself. The screenshot shows the Rating TableViewController.

my two files look like this and I want to get the rating stored for each Item/ Row so I can upload the rating afterwards in Firebase. Maybe you can help me connecting the missing dots in this :)

`class RatingItem { var key: String?

var item: String
var rating: Int

init?(item: String, rating: Int) {

        // Initialization should fail if there is no name or if the rating is negative.
        // The name must not be empty
        guard !item.isEmpty else {
            return nil
        }

        // The rating must be between 0 and 5 inclusively
        guard (rating >= 0) && (rating <= 7) else {
            return nil
        }

        // Initialize stored properties.
        self.item = item
        self.rating = rating

    }
var dictValue: [String: Any] {
    let createdAgo = kSecAttrCreationDate

    return ["Rating_Item" : item,
            "Rating_rating": rating,
            "created_ago": createdAgo]
}
init?(snapshot: DataSnapshot) {
    guard let dict = snapshot.value as? [String: Any],
    let item = dict["Rating_Item"] as? String,
    let rating = dict["Rating_rating"] as? Int

        else {return nil}

    self.key = snapshot.key
    self.item = item
    self.rating = rating

}   

} // MARK: - Extension

extension TeamMemberRatingViewController: UITableViewDataSource {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return ratingItems.count
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let ratingItemT = ratingItems[indexPath.row]

   }

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // Fetches the appropriate meal for the data source layout.
    let ratingItemT = ratingItems[indexPath.row]

    let cell = tableView.dequeueReusableCell(withIdentifier: "TeamMemberRatingTableViewCell", for: indexPath) as! TeamMemberRatingTableViewCell

    cell.itemLabel.text = ratingItemT.item

    return cell
}

} // MARK: - UITableViewDelegate

extension TeamMemberRatingViewController: UITableViewDelegate {

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
 return 80
}

}

`

Screenshot 2020-06-16 at 18 47 40
evgenyneu commented 4 years ago

Hi @manuG420, it's hard for me to debug a code here. Have you looked at the Performance screen of the demo app? It shows how to show rating in a table view and store/update the individual ratings.