evgenyneu / Cosmos

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

How to set the stars vertically instead of horizontally? #167

Open lsamaria opened 4 years ago

lsamaria commented 4 years ago

Please consider submitting the following information (if relevant):

How can I set the stars vertically, with the bottom star being 0 and the top star being 5 (in other words swiping up instead of from left to right)?

lazy var starRating: CosmosView = {
    let cosmosView = CosmosView()
    cosmosView.translatesAutoresizingMaskIntoConstraints = false
    cosmosView.rating = 0.0
    cosmosView.settings.starSize = 22.5
    cosmosView.settings.fillMode = .precise
    cosmosView.settings.minTouchRating = 0.0
    return cosmosView
}()

view.addSubview(starRating)
starRating.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
starRating.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
evgenyneu commented 4 years ago

Hi @lsamaria, this is not possible with Cosmos library, unfortunately. Sorry

lsamaria commented 4 years ago

@evgenyneu thanks. I was able to find a workaround using a CGAffineTransform:

cosmosView.frame.size = cosmosView.intrinsicContentSize
cosmosView.transform = CGAffineTransform(rotationAngle: -.pi * 0.5)

or

cosmosView.transform = CGAffineTransform(rotationAngle: -90 * .pi/180)

The two drawbacks are the stars get misaligned, they rotate clockwise a bit. I had to play with the image rotation to get them back but they are still a tad bit off. It's a good enough work around for now. The other draw back is the stars shrink from the original size. I had to play with the size and spacing to get them to realign again.

lazy var cosmosView: CosmosView = {

    let cosmosView = CosmosView()
    cosmosView.translatesAutoresizingMaskIntoConstraints = false

    cosmosView.settings.starSize = 27.5 // before I set the CGAffineTransform above, this was originally 22.5
    cosmosView.settings.fillMode = .precise
    cosmosView.settings.starMargin = 11.5 // before I set the CGAffineTransform above, this was originally 16
    cosmosView.settings.totalStars = 10

    let starFilledIcon = UIImage(named: "starFilledIcon")
    let rotatedImageFilledStar = starFilledIcon!.rotate(radians: .pi / 1.115)

    let starEmptyIcon = UIImage(named: "starEmptyIcon")
    let rotatedImageEmptyStar = starEmptyIcon!.rotate(radians: .pi / 1.115)

    cosmosView.settings.filledImage = rotatedImageFilledStar
    cosmosView.settings.emptyImage = rotatedImageEmptyStar

    return cosmosView
}()

extension UIImage {

    func rotate(radians: CGFloat) -> UIImage {
        let rotatedSize = CGRect(origin: .zero, size: size)
            .applying(CGAffineTransform(rotationAngle: CGFloat(radians)))
            .integral.size
        UIGraphicsBeginImageContext(rotatedSize)
        if let context = UIGraphicsGetCurrentContext() {
            let origin = CGPoint(x: rotatedSize.width / 2.0,
                                 y: rotatedSize.height / 2.0)
            context.translateBy(x: origin.x, y: origin.y)
            context.rotate(by: radians)
            draw(in: CGRect(x: -origin.y, y: -origin.x,
                            width: size.width, height: size.height))
            let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            return rotatedImage ?? self
        }

        return self
    }
}

Hopefully it helps someone else.

evgenyneu commented 4 years ago

Wow very cool, thanks for sharing!

lsamaria commented 4 years ago

np, thanks for the great library!