-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #669 from adevinta/pre-release/component/ratings
[RatingsDisplay] Copied RatingsDisplay to make a clean release of this component (from 0.8.0)
- Loading branch information
Showing
33 changed files
with
2,585 additions
and
0 deletions.
There are no files selected for viewing
18 changes: 18 additions & 0 deletions
18
...rces/Components/Rating/AccessibilityIdentifier/RatingDisplayAccessibilityIdentifier.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// | ||
// RatingDisplayAccessibilityIdentifier.swift | ||
// SparkCore | ||
// | ||
// Created by Michael Zimmermann on 21.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// The accessibility identifiers of the rating display. | ||
public enum RatingDisplayAccessibilityIdentifier { | ||
|
||
// MARK: - Properties | ||
|
||
/// The accessibility identifier. | ||
public static let identifier = "spark-rating-display" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// | ||
// CGLayerCache.swift | ||
// SparkCore | ||
// | ||
// Created by michael.zimmermann on 08.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import UIKit | ||
|
||
// sourcery: AutoMockable | ||
protocol CGLayerCaching { | ||
func object(forKey: NSString) -> CGLayer? | ||
func setObject(_ layer: CGLayer, forKey: NSString) | ||
} | ||
|
||
/// A simple facade for a static NSCache | ||
final class CGLayerCache: CGLayerCaching { | ||
private static var cache = NSCache<NSString, CGLayer>() | ||
|
||
func object(forKey key: NSString) -> CGLayer? { | ||
return Self.cache.object(forKey: key) | ||
} | ||
|
||
func setObject(_ layer: CGLayer, forKey key: NSString) { | ||
Self.cache.setObject(layer, forKey: key) | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
core/Sources/Components/Rating/Enum/RatingDisplaySize.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// | ||
// RatingDisplaySize.swift | ||
// SparkCore | ||
// | ||
// Created by Michael Zimmermann on 17.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
public enum RatingDisplaySize: Int, CaseIterable { | ||
case small = 12 | ||
case medium = 16 | ||
case input = 40 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// | ||
// RatingIntent.swift | ||
// SparkCore | ||
// | ||
// Created by michael.zimmermann on 09.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
public enum RatingIntent: CaseIterable { | ||
case main | ||
} |
14 changes: 14 additions & 0 deletions
14
core/Sources/Components/Rating/Enum/RatingStarsCount.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// | ||
// RatingStarsCount.swift | ||
// SparkCore | ||
// | ||
// Created by Michael Zimmermann on 17.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
public enum RatingStarsCount: Int, CaseIterable { | ||
case one = 1 | ||
case five = 5 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// | ||
// StarFillMode.swift | ||
// SparkCore | ||
// | ||
// Created by michael.zimmermann on 07.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// The star fill mode determins how the star is to be filled | ||
/// - full: the rating will be rounded to the next full number 0/1 and can be only empty or filled. | ||
/// - half: the raing will be rounded to the next half number and can either be empty, half filled and filled. | ||
/// - fraction: The fill rate will be the rating rounded to the neares fraction, e.g. half is fraction 2. | ||
/// - exact: The star will be filled with the exact rating value | ||
@frozen | ||
public enum StarFillMode { | ||
case full | ||
case half | ||
case fraction(_ :CGFloat) | ||
case exact | ||
|
||
// MARK: - Public functions | ||
/// function rating | ||
/// Calculate the rounded rating | ||
public func rating(of givenValue: CGFloat) -> CGFloat { | ||
if givenValue > 1 { | ||
return 1.0 | ||
} else if givenValue <= 0 { | ||
return 0.0 | ||
} | ||
|
||
switch self { | ||
case .full: return givenValue.rounded() | ||
case .half: return givenValue.halfRounded() | ||
case let .fraction(fraction): return givenValue.rounded(by: fraction) | ||
case .exact: return givenValue | ||
} | ||
} | ||
} | ||
|
||
// MARK: - Private helpers | ||
private extension CGFloat { | ||
func rounded(by fraction: CGFloat) -> CGFloat { | ||
return (self * fraction).rounded() / fraction | ||
} | ||
func halfRounded() -> CGFloat { | ||
return self.rounded(by: 2.0) | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
core/Sources/Components/Rating/Enum/StarFillModeUnitTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// | ||
// StarFillModeUnitTests.swift | ||
// SparkCoreUnitTests | ||
// | ||
// Created by michael.zimmermann on 08.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import XCTest | ||
|
||
@testable import SparkCore | ||
|
||
final class StarFillModeUnitTests: XCTestCase { | ||
|
||
func test_full_with_half_rating() { | ||
XCTAssertEqual(StarFillMode.full.rating(of: 0.5), 1.0) | ||
} | ||
|
||
func test_full_with_less_than_half_rating() { | ||
XCTAssertEqual(StarFillMode.full.rating(of: 0.49), 0.0) | ||
} | ||
|
||
func test_half_with_half_rating() { | ||
XCTAssertEqual(StarFillMode.half.rating(of: 0.6), 0.5) | ||
} | ||
|
||
func test_half_with_big_rating() { | ||
XCTAssertEqual(StarFillMode.half.rating(of: 0.75), 1.0) | ||
} | ||
|
||
func test_half_with_small_rating() { | ||
XCTAssertEqual(StarFillMode.half.rating(of: 0.2), 0.0) | ||
} | ||
|
||
func test_exact_rating() { | ||
XCTAssertEqual(StarFillMode.exact.rating(of: 0.211), 0.211) | ||
} | ||
|
||
func test_fraction_rating_round_down() { | ||
XCTAssertEqual(StarFillMode.fraction(10).rating(of: 0.1111), 0.1) | ||
} | ||
|
||
func test_fraction_rating_round_up() { | ||
XCTAssertEqual(StarFillMode.fraction(10).rating(of: 0.15), 0.2) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// | ||
// ShapeLayer.swift | ||
// SparkCore | ||
// | ||
// Created by michael.zimmermann on 08.11.23. | ||
// Copyright © 2023 Adevinta. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import UIKit | ||
|
||
/// The Shape Layer draws a shape onto a layer and returns the CGLayer. | ||
final class ShapeLayer { | ||
// MARK: - Private variables | ||
private let shape: CGPathShape | ||
private let fillColor: CGColor | ||
private let strokeColor: CGColor | ||
private let fillPercentage: CGFloat | ||
private let strokeWidth: CGFloat | ||
|
||
// MARK: - Initializer | ||
init(shape: CGPathShape, | ||
fillColor: CGColor, | ||
strokeColor: CGColor, | ||
fillPercentage: CGFloat, | ||
strokeWidth: CGFloat) { | ||
self.shape = shape | ||
self.fillColor = fillColor | ||
self.strokeColor = strokeColor | ||
self.fillPercentage = fillPercentage | ||
self.strokeWidth = strokeWidth | ||
} | ||
|
||
/// Create a CGLayer and draw the shape on it. | ||
func layer(graphicsContext: CGContext, size: CGSize) -> CGLayer { | ||
guard let starLayer = CGLayer(graphicsContext, size: size, auxiliaryInfo: nil) else { | ||
fatalError("Couldn't create layer") | ||
} | ||
|
||
guard let context = starLayer.context else { | ||
fatalError("Couldn't create layer") | ||
} | ||
|
||
self.drawShape(graphicsContext: context, size: size) | ||
return starLayer | ||
} | ||
|
||
// MARK: - Private | ||
private func drawShape(graphicsContext: CGContext, size: CGSize) { | ||
let rect = CGRect(origin: .zero, size: size) | ||
let path = self.shape.cgPath(rect: rect) | ||
|
||
graphicsContext.saveGState() | ||
|
||
graphicsContext.clip(to: rect) | ||
graphicsContext.addPath(path) | ||
graphicsContext.setLineWidth(self.strokeWidth) | ||
graphicsContext.setStrokeColor(self.strokeColor) | ||
graphicsContext.drawPath(using: .stroke) | ||
|
||
let insets = self.shape.insets.withHorizontalPadding(self.strokeWidth/2.0) | ||
let maskWidth = CGFloat((insets.right - insets.left) * fillPercentage) | ||
|
||
let maskHeight = rect.height | ||
|
||
let clipRect = CGRect( | ||
x: insets.left, | ||
y: 0, | ||
width: maskWidth, | ||
height: maskHeight | ||
) | ||
graphicsContext.clip(to: clipRect) | ||
graphicsContext.addPath(path) | ||
graphicsContext.setFillColor(self.fillColor) | ||
graphicsContext.drawPath(using: .fill) | ||
|
||
graphicsContext.addPath(path) | ||
graphicsContext.setStrokeColor(self.fillColor) | ||
graphicsContext.setLineWidth(self.strokeWidth) | ||
graphicsContext.drawPath(using: .stroke) | ||
|
||
graphicsContext.restoreGState() | ||
} | ||
} | ||
|
||
// MARK: Private extensions | ||
private extension UIEdgeInsets { | ||
func withHorizontalPadding(_ padding: CGFloat) -> UIEdgeInsets { | ||
return UIEdgeInsets(top: self.top, left: self.left - padding, bottom: self.bottom, right: self.right + padding) | ||
} | ||
} |
Oops, something went wrong.