├── example.png
├── example_dark.png
├── StarRatingExample
├── StarRatingExample
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── StarRatingExampleApp.swift
│ ├── ColorExtension.swift
│ ├── Info.plist
│ └── ContentView.swift
└── StarRatingExample.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
│ └── project.pbxproj
├── Tests
├── LinuxMain.swift
└── StarRatingTests
│ ├── XCTestManifests.swift
│ └── StarRatingTests.swift
├── Sources
└── StarRating
│ ├── Extensions
│ ├── CGRectExtension.swift
│ ├── CGVectorExtension.swift
│ ├── CGPoint.swift
│ └── ColorExtension.swift
│ ├── Shapes
│ └── Star.swift
│ ├── StarRatingConfiguration.swift
│ └── StarRating.swift
├── .github
└── FUNDING.yml
├── LICENSE
├── Package.swift
├── .gitignore
└── README.md
/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dkk/StarRating/HEAD/example.png
--------------------------------------------------------------------------------
/example_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dkk/StarRating/HEAD/example_dark.png
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import StarRatingTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += StarRatingTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sources/StarRating/Extensions/CGRectExtension.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | extension CGRect {
4 | var center: CGPoint {
5 | return CGPoint(x: width / 2, y: height / 2)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Sources/StarRating/Extensions/CGVectorExtension.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | extension CGVector {
4 | func scale(by scalar: CGFloat) -> CGVector {
5 | CGVector(dx: dx * scalar, dy: dy * scalar)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Tests/StarRatingTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(StarRatingTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/StarRatingExampleApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct StarRatingExampleApp: App {
5 | var body: some Scene {
6 | WindowGroup {
7 | ContentView()
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Sources/StarRating/Extensions/CGPoint.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | extension CGPoint {
4 | // Creates a point from the components of the resulting unit vector
5 | init(angle: CGFloat) {
6 | self = CGPoint(x: cos(angle), y: sin(angle))
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "StarRating",
6 | "repositoryURL": "https://github.com/dkk/StarRating",
7 | "state": {
8 | "branch": "main",
9 | "revision": "7c71a88e231e0b60bf435674bd08833725cb1b4c",
10 | "version": null
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Tests/StarRatingTests/StarRatingTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import StarRating
3 |
4 | final class StarRatingTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(StarRating().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/StarRating/Extensions/ColorExtension.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | public extension Color {
4 | struct StarRating {
5 | private static let defaultLightShadow = UIColor(white: 0, alpha: 0.33)
6 | private static let defaultDarkShadow = UIColor(white: 1, alpha: 0.33)
7 |
8 | /// The color of the default star border shadow
9 | static public var defaultShadow: Color {
10 | Color(.init { $0.userInterfaceStyle == .light ? Self.defaultLightShadow : Self.defaultDarkShadow })
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [dkk] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/ColorExtension.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | extension Color {
4 | struct Example {
5 | static var border: Color {
6 | Color(.init { $0.userInterfaceStyle == .light ? .black : .white })
7 | }
8 |
9 | static var shadow: Color {
10 | Color(.init { $0.userInterfaceStyle == .light ? UIColor.black.withAlphaComponent(0.33) : UIColor.white.withAlphaComponent(0.33) })
11 | }
12 | }
13 |
14 | static var random: Color {
15 | Color(red: Double.random(in: 0...1), green: Double.random(in: 0...1), blue: Double.random(in: 0...1))
16 | }
17 | }
18 |
19 | extension Array where Element == Color {
20 | static var random: [Color] {
21 | var colors = [Color]()
22 | for _ in (0 ..< Int.random(in: 1...6)) {
23 | colors += [Color.random]
24 | }
25 | return colors
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Daniel Klöck
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "StarRating",
8 | platforms: [
9 | .iOS(.v13)
10 | ],
11 | products: [
12 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
13 | .library(
14 | name: "StarRating",
15 | targets: ["StarRating"]),
16 | ],
17 | dependencies: [
18 | // Dependencies declare other packages that this package depends on.
19 | // .package(url: /* package url */, from: "1.0.0"),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
23 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
24 | .target(
25 | name: "StarRating",
26 | dependencies: []),
27 | .testTarget(
28 | name: "StarRatingTests",
29 | dependencies: ["StarRating"]),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Sources/StarRating/Shapes/Star.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | /// A Shape with the form of Star Polygon
4 | public struct Star: Shape {
5 | let vertices: Int
6 | let weight: CGFloat
7 |
8 | /// - Parameters:
9 | /// - vertices: The numer of vertices of the star (only outer points are counted)
10 | /// - weight: Defines the position of the inner points. 0: inner points == center,
11 | /// 1: inner points are at the height of the outer points
12 | public init(vertices: Int = 5, weight: CGFloat = 0.45) {
13 | self.vertices = vertices
14 | self.weight = weight
15 | }
16 |
17 | public func path(in rect: CGRect) -> Path {
18 | guard vertices > 1 else { return Path() }
19 |
20 | var angle: CGFloat = .pi / -2
21 |
22 | // angle from a vertex to an inner point
23 | let sectionAngle: CGFloat = .pi / CGFloat(vertices)
24 |
25 | let innerPointMovement = CGVector(dx: rect.center.x,
26 | dy: rect.center.y).scale(by: weight)
27 |
28 | let startingPoint = CGPoint(x: 0, y: -rect.center.y)
29 |
30 | var path = Path()
31 | path.move(to: startingPoint)
32 |
33 | var lowestPointY: CGFloat = 0
34 |
35 | for vertexIndex in 0 ..< vertices * 2 {
36 | let unitMovement = CGPoint(angle: angle)
37 | let point: CGPoint
38 |
39 | if vertexIndex % 2 == 0 {
40 | //position of next outer point
41 | point = CGPoint(x: rect.center.x * unitMovement.x, y: rect.center.y * unitMovement.y)
42 | } else {
43 | //position of next inner point
44 | point = CGPoint(x: innerPointMovement.dx * unitMovement.x, y: innerPointMovement.dy * unitMovement.y)
45 | }
46 | path.addLine(to: point)
47 |
48 | if point.y > lowestPointY {
49 | lowestPointY = point.y
50 | }
51 |
52 | angle += sectionAngle
53 | }
54 | path.closeSubpath()
55 |
56 | let unusedSpace = (rect.height / 2 - lowestPointY) / 2
57 | // center the star vertically
58 | let transform = CGAffineTransform(translationX: rect.center.x, y: rect.center.y + unusedSpace)
59 | return path.applying(transform)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
92 | # From SPM .gitignore
93 | .DS_Store
94 | /.build
95 | /Packages
96 | /*.xcodeproj
97 | xcuserdata/
98 |
99 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # StarRating
2 |
3 |  
4 |
5 | This is a customizable star rating control written specifically for SwiftUI. It shows a star rating and handles user input.
6 | It also contains a star Shape with customizable number of vertices and weight.
7 |
8 | ## Installation
9 | Requirements iOS 13+
10 |
11 | ### Swift Package Manager
12 | 1. In Xcode, open your project and navigate to File → Swift Packages → Add Package Dependency.
13 | 2. Paste the repository URL (https://github.com/dkk/StarRating) and click Next.
14 | 3. For Rules, select version.
15 | 4. Click Finish.
16 |
17 | ### Swift Package
18 | ```swift
19 | .package(url: "https://github.com/dkk/StarRating", .upToNextMajor(from: "1.2.0"))
20 | ```
21 |
22 | ## Usage
23 |
24 | Import StarRating package to your view.
25 |
26 | ```swift
27 | import StarRating
28 | ```
29 |
30 | and you are ready to use `StarRating` or the `Shape` `Star` in you SwiftUI code.
31 |
32 | ### Use StarRating to display a rating
33 | You can display a rating with the line:
34 | ```swift
35 | // set the rating you want to display as initialRating
36 | StarRating(initialRating: 3.7)
37 | ```
38 |
39 | ### Use StarRating to get a rating from the user
40 | To show a fully functional star rating that can handle user input, add the line:
41 | ```swift
42 | // set the initialRating
43 | // add a callback to do something with the new rating
44 | StarRating(initialRating: 0, onRatingChanged: { print($0) })
45 | ```
46 |
47 | ### Configure StarRating
48 | To configure the control, `StarRating` binds a `StarRatingConfiguration` object. This makes it easy to dynamically change the control's style and behaviour.
49 |
50 | 1. Create a `StarRatingConfiguration` object:
51 | ```swift
52 | @State var customConfig = StarRatingConfiguration(minRating: 2, numberOfStars: 10)
53 | ```
54 |
55 | 2. Pass it when creating the instance of `StarRating` and update it later as needed:
56 | ```swift
57 | StarRating(initialRating: 2.0, configuration: $customConfig) { newRating in
58 | customConfig.starVertices = Int(newRating)
59 | }
60 | ```
61 |
62 | Read the implementation of [StarRatingConfiguration](Sources/StarRating/StarRatingConfiguration.swift) to see what configuration posibilities are available at the moment.
63 |
64 | ### Use the Star Shape
65 | Use `Star` the same way as you would use standard SwiftUI Shapes, such as `Rectangle`. `Star` allows to configure the number of vertices and the weight. Example usage:
66 | ```swift
67 | Star(vertices: 5, weight: 0.45)
68 | ```
69 |
70 | ## Contribute
71 | You can contribute to this project by helping me solve any [reported issues or feature requests](https://github.com/dkk/WrappingHStack/issues) and creating a pull request.
72 |
73 | ## Support
74 | If you just want to say thanks, you could [buy me a coffee ☕️](https://www.buymeacoffee.com/kloeck).
75 |
76 | ## License
77 | StarRating is released under the [MIT License](LICENSE).
78 |
--------------------------------------------------------------------------------
/Sources/StarRating/StarRatingConfiguration.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | /// This class can be used to configure StarRating's style & behaviour
4 | public class StarRatingConfiguration: ObservableObject {
5 | @Published public var spacing: CGFloat
6 | @Published public var numberOfStars: Int
7 | @Published public var stepType: StarRating.StepType
8 | @Published public var minRating: Double
9 | @Published public var borderWidth: CGFloat
10 | @Published public var borderColor: Color
11 | @Published public var emptyColor: Color
12 | @Published public var shadowRadius: CGFloat
13 | @Published public var shadowColor: Color
14 | @Published public var fillColors: [Color]
15 | @Published public var starVertices: Int
16 | @Published public var starWeight: CGFloat
17 |
18 | /// - Parameters:
19 | /// - spacing: spacing between stars
20 | /// - numberOfStars: number of stars (note that changing this value changes the max rating value)
21 | /// - stepType: .full will allow full star ratings, .half will also allow half stars
22 | /// - minRating: the minimal rating allowed
23 | /// - borderWidth: the width of the border of the stars
24 | /// - borderColor: the color of the border
25 | /// - emptyColor: empty star color
26 | /// - shadowRadius: the radius of the star border shadow
27 | /// - shadowColor: the color of the star border shadow
28 | /// - fillColors: the colors of the gradient used to fill the stars
29 | /// - starVertices: The numer of vertices of the star (only outer points are counted),
30 | /// must be at least 2 to show something
31 | /// - starWeight: Defines the position of the inner points. 0: inner points == center,
32 | /// 1: inner points are at the height of the outer points
33 | public init (
34 | spacing: CGFloat = 12,
35 | numberOfStars: Int = 5,
36 | stepType: StarRating.StepType = .half,
37 | minRating: Double = 0,
38 | borderWidth: CGFloat = 2,
39 | borderColor: Color = .white,
40 | emptyColor: Color = .clear,
41 | shadowRadius: CGFloat = 4,
42 | shadowColor: Color = Color.StarRating.defaultShadow,
43 | fillColors: [Color] = [.yellow, .orange],
44 | starVertices: Int = 5,
45 | starWeight: CGFloat = 0.45
46 | ) {
47 | self.spacing = spacing
48 | self.numberOfStars = numberOfStars
49 | self.stepType = stepType
50 | self.borderWidth = borderWidth
51 | self.borderColor = borderColor
52 | self.emptyColor = emptyColor
53 | self.shadowRadius = shadowRadius
54 | self.shadowColor = shadowColor
55 | self.fillColors = fillColors
56 | self.starVertices = starVertices
57 | self.starWeight = starWeight
58 | self.minRating = StarRating.normalizedRating(rating: minRating,
59 | minRating: 0,
60 | numberOfStars: numberOfStars,
61 | stepType: stepType)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import StarRating
3 |
4 | struct ContentView: View {
5 | var bigStar: some View {
6 | ZStack {
7 | Star(vertices: 5, weight: 0.45)
8 | .fill(LinearGradient(
9 | gradient: .init(colors: [.pink, .blue]),
10 | startPoint: .init(x: 0, y: 0),
11 | endPoint: .init(x: 1, y: 1)
12 | ))
13 | .frame(width: 300, height: 300, alignment: .center)
14 | .overlay(Star().stroke(Color.white, lineWidth: 4))
15 | .shadow(
16 | color: Color.Example.shadow,
17 | radius: 7
18 | )
19 |
20 | Text("StarRating")
21 | .font(.largeTitle)
22 | .foregroundColor(.white)
23 | .offset(x: 0, y: -6)
24 | }
25 | }
26 |
27 | @State var customConfig = StarRatingConfiguration(spacing: 8,
28 | numberOfStars: 10,
29 | stepType: .exact,
30 | minRating: 2,
31 | borderWidth: 1,
32 | borderColor: Color.Example.border,
33 | emptyColor: Color.Example.border.opacity(0.15),
34 | shadowRadius: 0,
35 | fillColors: [Color].random,
36 | starVertices: 6,
37 | starWeight: 0.6)
38 |
39 | var body: some View {
40 | VStack {
41 | // Example of using the Star Shape
42 | bigStar
43 |
44 | // Example of using StarRating with default configuration
45 | StarRating(initialRating: 3.7, onRatingChanged: { print($0) })
46 | .frame(width: 300, height: 50)
47 | .animation(.linear)
48 |
49 | // Example of using StarRating with custom configuration & with live updates
50 | StarRating(initialRating: 2.0, configuration: $customConfig) { newRating in
51 | customConfig.starVertices = Int(newRating)
52 | customConfig.fillColors = [Color].random
53 | print(newRating)
54 | }
55 |
56 | Spacer()
57 |
58 | // Example of using StarRating as display only
59 | StarRating(initialRating: 3.7)
60 | .frame(width: 300, height: 50)
61 | }
62 | }
63 | }
64 |
65 | struct ContentView_Previews: PreviewProvider {
66 | static var previews: some View {
67 | Group {
68 | ContentView()
69 | .padding()
70 | .previewLayout(.sizeThatFits)
71 |
72 | ContentView()
73 | .padding()
74 | .environment(\.colorScheme, .dark)
75 | .background(Color(red: 0.188, green: 0.192, blue: 0.208, opacity: 1.000))
76 | .previewLayout(.sizeThatFits)
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Sources/StarRating/StarRating.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | /// A customizable star rating element It shows a star rating and handles user input.
4 | public struct StarRating: View {
5 | /// clips the rating to the allowed interval and rounds it
6 | /// depending on the stepType
7 | public static func normalizedRating(rating: Double, minRating: Double, numberOfStars: Int, stepType: StepType) -> Double {
8 | let ratingInInterval = min(max(rating, minRating), Double(numberOfStars))
9 | switch stepType {
10 | case .half: return round(ratingInInterval * 2) / 2
11 | case .full: return round(ratingInInterval)
12 | case .exact: return ratingInInterval
13 | }
14 | }
15 |
16 | public enum StepType {
17 | case full, half, exact
18 | }
19 |
20 | /// The configuration of the StarRating control.
21 | /// Allows you to customize style and behaviour
22 | @Binding public var configuration: StarRatingConfiguration
23 |
24 | /// The currently selected rating
25 | @State public var rating: Double
26 |
27 | /// Gets called when the user changes the rating by tapping or dragging
28 | private var onRatingChanged: ((Double) -> Void)?
29 |
30 | /// - Parameters:
31 | /// - initialRating: The initial rating value
32 | /// - configuration: The configuration of the StarRating control.
33 | /// Allows you to customize style and behaviour
34 | /// - onRatingChanged: Gets called when the user changes the rating
35 | /// by tapping or dragging
36 | public init (
37 | initialRating: Double,
38 | configuration: Binding = .constant(StarRatingConfiguration()),
39 | onRatingChanged: ((Double) -> Void)? = nil
40 | ) {
41 | self.onRatingChanged = onRatingChanged
42 |
43 | _configuration = configuration
44 | let normalizedRating = StarRating.normalizedRating(
45 | rating: initialRating,
46 | minRating: configuration.wrappedValue.minRating,
47 | numberOfStars: configuration.wrappedValue.numberOfStars,
48 | stepType: configuration.wrappedValue.stepType
49 | )
50 | _rating = State(initialValue: normalizedRating)
51 | }
52 |
53 | private var starBorder: some View {
54 | Star(
55 | vertices: configuration.starVertices,
56 | weight: configuration.starWeight
57 | )
58 | .stroke(configuration.borderColor,
59 | lineWidth: configuration.borderWidth)
60 | .aspectRatio(contentMode: .fit)
61 | }
62 |
63 | private var starBackground: some View {
64 | Star(
65 | vertices: configuration.starVertices,
66 | weight: configuration.starWeight
67 | )
68 | .fill(configuration.emptyColor)
69 | .aspectRatio(contentMode: .fit)
70 | }
71 |
72 | private var starFilling: some View {
73 | Star(
74 | vertices: configuration.starVertices,
75 | weight: configuration.starWeight
76 | )
77 | .fill(LinearGradient(
78 | gradient: .init(colors: configuration.fillColors),
79 | startPoint: .init(x: 0, y: 0),
80 | endPoint: .init(x: 1, y: 1)
81 | ))
82 | .aspectRatio(contentMode: .fit)
83 | }
84 |
85 | private func updateRatingIfNeeded(width: CGFloat, marginSize: CGFloat, xLocation: CGFloat) {
86 | guard let onRatingChanged = onRatingChanged else { return }
87 |
88 | let widthWithoutMargin = width - marginSize * 2
89 | let numberOfSpaces = CGFloat(configuration.numberOfStars - 1)
90 | let starWidth = (widthWithoutMargin - configuration.spacing * numberOfSpaces) / CGFloat(configuration.numberOfStars)
91 |
92 | guard starWidth > 0 else { return }
93 |
94 | var newRating: CGFloat?
95 | var minLoc = marginSize
96 | var idx = 0
97 | repeat {
98 | let lowBound = CGFloat(idx)
99 | let loc = xLocation - minLoc
100 | if loc <= starWidth {
101 | let percentOfStar = max(loc, 0) / starWidth
102 | newRating = lowBound + percentOfStar
103 | } else {
104 | minLoc += starWidth + configuration.spacing
105 | idx += 1
106 |
107 | if idx >= configuration.numberOfStars {
108 | newRating = CGFloat(configuration.numberOfStars)
109 | }
110 | }
111 | } while (newRating == nil)
112 |
113 | let normalizedRating = Self.normalizedRating(rating: Double(newRating!),
114 | minRating: configuration.minRating,
115 | numberOfStars: configuration.numberOfStars,
116 | stepType: configuration.stepType)
117 |
118 | if normalizedRating != rating {
119 | rating = normalizedRating
120 | onRatingChanged(rating)
121 | }
122 | }
123 |
124 | private func ratingWidth(fullWidth: CGFloat, horizontalPadding: CGFloat) -> CGFloat {
125 | let widthWithoutMargin = fullWidth - horizontalPadding * 2
126 | let numberOfSpaces = CGFloat(configuration.numberOfStars - 1)
127 | let starWidth = (widthWithoutMargin - configuration.spacing * numberOfSpaces) / CGFloat(configuration.numberOfStars)
128 |
129 | return CGFloat(rating) * starWidth + floor(CGFloat(rating)) * configuration.spacing
130 | }
131 |
132 | public var body: some View {
133 | GeometryReader { geo in
134 | let horizontalPadding = geo.size.width / CGFloat(configuration.numberOfStars * 2 + 2)
135 |
136 | let maskWidth = ratingWidth(fullWidth:geo.size.width,
137 | horizontalPadding: horizontalPadding)
138 |
139 | let drag = DragGesture(minimumDistance: 0).onChanged { value in
140 | updateRatingIfNeeded(width: geo.size.width,
141 | marginSize: horizontalPadding,
142 | xLocation: value.location.x)
143 | }
144 |
145 | ZStack {
146 | HStack(spacing: configuration.spacing) {
147 | ForEach((0 ..< configuration.numberOfStars), id: \.self) { index in
148 |
149 | starBorder
150 | .shadow(color: configuration.shadowColor, radius: configuration.shadowRadius)
151 | .background(starBackground)
152 | }
153 | }
154 |
155 | HStack(spacing: configuration.spacing) {
156 | ForEach((0 ..< configuration.numberOfStars), id: \.self) { index in
157 |
158 | starFilling
159 | .mask(Rectangle().size(width: maskWidth, height: geo.size.height))
160 | .overlay(starBorder)
161 | }
162 | }
163 | .mask(Rectangle().size(width: maskWidth, height: geo.size.height))
164 | }
165 | .padding(.horizontal, horizontalPadding)
166 | .contentShape(Rectangle())
167 | .gesture(drag)
168 | }
169 | }
170 | }
171 |
172 | struct StarRating_Previews: PreviewProvider {
173 | static var previews: some View {
174 | StarRating(initialRating: 2.3)
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/StarRatingExample/StarRatingExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | AF487D1A25D44B8300E8B4BD /* StarRatingExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF487D1925D44B8300E8B4BD /* StarRatingExampleApp.swift */; };
11 | AF487D1C25D44B8300E8B4BD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF487D1B25D44B8300E8B4BD /* ContentView.swift */; };
12 | AF487D1E25D44B8400E8B4BD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AF487D1D25D44B8400E8B4BD /* Assets.xcassets */; };
13 | AF487D2125D44B8400E8B4BD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AF487D2025D44B8400E8B4BD /* Preview Assets.xcassets */; };
14 | AF487D2B25D44D0500E8B4BD /* StarRating in Frameworks */ = {isa = PBXBuildFile; productRef = AF487D2A25D44D0500E8B4BD /* StarRating */; };
15 | AFF91FF925D573380080E1DF /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFF91FF825D573380080E1DF /* ColorExtension.swift */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXFileReference section */
19 | AF487D1625D44B8300E8B4BD /* StarRatingExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StarRatingExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
20 | AF487D1925D44B8300E8B4BD /* StarRatingExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarRatingExampleApp.swift; sourceTree = ""; };
21 | AF487D1B25D44B8300E8B4BD /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
22 | AF487D1D25D44B8400E8B4BD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
23 | AF487D2025D44B8400E8B4BD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
24 | AF487D2225D44B8400E8B4BD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
25 | AF487D3225D44EA000E8B4BD /* StarRating */ = {isa = PBXFileReference; lastKnownFileType = folder; name = StarRating; path = ..; sourceTree = ""; };
26 | AFF91FF825D573380080E1DF /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = ""; };
27 | /* End PBXFileReference section */
28 |
29 | /* Begin PBXFrameworksBuildPhase section */
30 | AF487D1325D44B8300E8B4BD /* Frameworks */ = {
31 | isa = PBXFrameworksBuildPhase;
32 | buildActionMask = 2147483647;
33 | files = (
34 | AF487D2B25D44D0500E8B4BD /* StarRating in Frameworks */,
35 | );
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXFrameworksBuildPhase section */
39 |
40 | /* Begin PBXGroup section */
41 | AF487D0D25D44B8300E8B4BD = {
42 | isa = PBXGroup;
43 | children = (
44 | AF487D3225D44EA000E8B4BD /* StarRating */,
45 | AF487D1825D44B8300E8B4BD /* StarRatingExample */,
46 | AF487D1725D44B8300E8B4BD /* Products */,
47 | );
48 | sourceTree = "";
49 | };
50 | AF487D1725D44B8300E8B4BD /* Products */ = {
51 | isa = PBXGroup;
52 | children = (
53 | AF487D1625D44B8300E8B4BD /* StarRatingExample.app */,
54 | );
55 | name = Products;
56 | sourceTree = "";
57 | };
58 | AF487D1825D44B8300E8B4BD /* StarRatingExample */ = {
59 | isa = PBXGroup;
60 | children = (
61 | AFF91FF825D573380080E1DF /* ColorExtension.swift */,
62 | AF487D1925D44B8300E8B4BD /* StarRatingExampleApp.swift */,
63 | AF487D1B25D44B8300E8B4BD /* ContentView.swift */,
64 | AF487D1D25D44B8400E8B4BD /* Assets.xcassets */,
65 | AF487D2225D44B8400E8B4BD /* Info.plist */,
66 | AF487D1F25D44B8400E8B4BD /* Preview Content */,
67 | );
68 | path = StarRatingExample;
69 | sourceTree = "";
70 | };
71 | AF487D1F25D44B8400E8B4BD /* Preview Content */ = {
72 | isa = PBXGroup;
73 | children = (
74 | AF487D2025D44B8400E8B4BD /* Preview Assets.xcassets */,
75 | );
76 | path = "Preview Content";
77 | sourceTree = "";
78 | };
79 | /* End PBXGroup section */
80 |
81 | /* Begin PBXNativeTarget section */
82 | AF487D1525D44B8300E8B4BD /* StarRatingExample */ = {
83 | isa = PBXNativeTarget;
84 | buildConfigurationList = AF487D2525D44B8400E8B4BD /* Build configuration list for PBXNativeTarget "StarRatingExample" */;
85 | buildPhases = (
86 | AF487D1225D44B8300E8B4BD /* Sources */,
87 | AF487D1325D44B8300E8B4BD /* Frameworks */,
88 | AF487D1425D44B8300E8B4BD /* Resources */,
89 | );
90 | buildRules = (
91 | );
92 | dependencies = (
93 | );
94 | name = StarRatingExample;
95 | packageProductDependencies = (
96 | AF487D2A25D44D0500E8B4BD /* StarRating */,
97 | );
98 | productName = StarRatingExample;
99 | productReference = AF487D1625D44B8300E8B4BD /* StarRatingExample.app */;
100 | productType = "com.apple.product-type.application";
101 | };
102 | /* End PBXNativeTarget section */
103 |
104 | /* Begin PBXProject section */
105 | AF487D0E25D44B8300E8B4BD /* Project object */ = {
106 | isa = PBXProject;
107 | attributes = {
108 | LastSwiftUpdateCheck = 1240;
109 | LastUpgradeCheck = 1240;
110 | TargetAttributes = {
111 | AF487D1525D44B8300E8B4BD = {
112 | CreatedOnToolsVersion = 12.4;
113 | };
114 | };
115 | };
116 | buildConfigurationList = AF487D1125D44B8300E8B4BD /* Build configuration list for PBXProject "StarRatingExample" */;
117 | compatibilityVersion = "Xcode 9.3";
118 | developmentRegion = en;
119 | hasScannedForEncodings = 0;
120 | knownRegions = (
121 | en,
122 | Base,
123 | );
124 | mainGroup = AF487D0D25D44B8300E8B4BD;
125 | packageReferences = (
126 | AF487D2925D44D0500E8B4BD /* XCRemoteSwiftPackageReference "StarRating" */,
127 | );
128 | productRefGroup = AF487D1725D44B8300E8B4BD /* Products */;
129 | projectDirPath = "";
130 | projectRoot = "";
131 | targets = (
132 | AF487D1525D44B8300E8B4BD /* StarRatingExample */,
133 | );
134 | };
135 | /* End PBXProject section */
136 |
137 | /* Begin PBXResourcesBuildPhase section */
138 | AF487D1425D44B8300E8B4BD /* Resources */ = {
139 | isa = PBXResourcesBuildPhase;
140 | buildActionMask = 2147483647;
141 | files = (
142 | AF487D2125D44B8400E8B4BD /* Preview Assets.xcassets in Resources */,
143 | AF487D1E25D44B8400E8B4BD /* Assets.xcassets in Resources */,
144 | );
145 | runOnlyForDeploymentPostprocessing = 0;
146 | };
147 | /* End PBXResourcesBuildPhase section */
148 |
149 | /* Begin PBXSourcesBuildPhase section */
150 | AF487D1225D44B8300E8B4BD /* Sources */ = {
151 | isa = PBXSourcesBuildPhase;
152 | buildActionMask = 2147483647;
153 | files = (
154 | AFF91FF925D573380080E1DF /* ColorExtension.swift in Sources */,
155 | AF487D1C25D44B8300E8B4BD /* ContentView.swift in Sources */,
156 | AF487D1A25D44B8300E8B4BD /* StarRatingExampleApp.swift in Sources */,
157 | );
158 | runOnlyForDeploymentPostprocessing = 0;
159 | };
160 | /* End PBXSourcesBuildPhase section */
161 |
162 | /* Begin XCBuildConfiguration section */
163 | AF487D2325D44B8400E8B4BD /* Debug */ = {
164 | isa = XCBuildConfiguration;
165 | buildSettings = {
166 | ALWAYS_SEARCH_USER_PATHS = NO;
167 | CLANG_ANALYZER_NONNULL = YES;
168 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
169 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
170 | CLANG_CXX_LIBRARY = "libc++";
171 | CLANG_ENABLE_MODULES = YES;
172 | CLANG_ENABLE_OBJC_ARC = YES;
173 | CLANG_ENABLE_OBJC_WEAK = YES;
174 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
175 | CLANG_WARN_BOOL_CONVERSION = YES;
176 | CLANG_WARN_COMMA = YES;
177 | CLANG_WARN_CONSTANT_CONVERSION = YES;
178 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
179 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
180 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
181 | CLANG_WARN_EMPTY_BODY = YES;
182 | CLANG_WARN_ENUM_CONVERSION = YES;
183 | CLANG_WARN_INFINITE_RECURSION = YES;
184 | CLANG_WARN_INT_CONVERSION = YES;
185 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
186 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
187 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
188 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
189 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
190 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
191 | CLANG_WARN_STRICT_PROTOTYPES = YES;
192 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
193 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
194 | CLANG_WARN_UNREACHABLE_CODE = YES;
195 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
196 | COPY_PHASE_STRIP = NO;
197 | DEBUG_INFORMATION_FORMAT = dwarf;
198 | ENABLE_STRICT_OBJC_MSGSEND = YES;
199 | ENABLE_TESTABILITY = YES;
200 | GCC_C_LANGUAGE_STANDARD = gnu11;
201 | GCC_DYNAMIC_NO_PIC = NO;
202 | GCC_NO_COMMON_BLOCKS = YES;
203 | GCC_OPTIMIZATION_LEVEL = 0;
204 | GCC_PREPROCESSOR_DEFINITIONS = (
205 | "DEBUG=1",
206 | "$(inherited)",
207 | );
208 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
209 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
210 | GCC_WARN_UNDECLARED_SELECTOR = YES;
211 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
212 | GCC_WARN_UNUSED_FUNCTION = YES;
213 | GCC_WARN_UNUSED_VARIABLE = YES;
214 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
215 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
216 | MTL_FAST_MATH = YES;
217 | ONLY_ACTIVE_ARCH = YES;
218 | SDKROOT = iphoneos;
219 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
220 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
221 | };
222 | name = Debug;
223 | };
224 | AF487D2425D44B8400E8B4BD /* Release */ = {
225 | isa = XCBuildConfiguration;
226 | buildSettings = {
227 | ALWAYS_SEARCH_USER_PATHS = NO;
228 | CLANG_ANALYZER_NONNULL = YES;
229 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
231 | CLANG_CXX_LIBRARY = "libc++";
232 | CLANG_ENABLE_MODULES = YES;
233 | CLANG_ENABLE_OBJC_ARC = YES;
234 | CLANG_ENABLE_OBJC_WEAK = YES;
235 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
236 | CLANG_WARN_BOOL_CONVERSION = YES;
237 | CLANG_WARN_COMMA = YES;
238 | CLANG_WARN_CONSTANT_CONVERSION = YES;
239 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
241 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
242 | CLANG_WARN_EMPTY_BODY = YES;
243 | CLANG_WARN_ENUM_CONVERSION = YES;
244 | CLANG_WARN_INFINITE_RECURSION = YES;
245 | CLANG_WARN_INT_CONVERSION = YES;
246 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
247 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
248 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
249 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
250 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
251 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
252 | CLANG_WARN_STRICT_PROTOTYPES = YES;
253 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
254 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
255 | CLANG_WARN_UNREACHABLE_CODE = YES;
256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
257 | COPY_PHASE_STRIP = NO;
258 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
259 | ENABLE_NS_ASSERTIONS = NO;
260 | ENABLE_STRICT_OBJC_MSGSEND = YES;
261 | GCC_C_LANGUAGE_STANDARD = gnu11;
262 | GCC_NO_COMMON_BLOCKS = YES;
263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
265 | GCC_WARN_UNDECLARED_SELECTOR = YES;
266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
267 | GCC_WARN_UNUSED_FUNCTION = YES;
268 | GCC_WARN_UNUSED_VARIABLE = YES;
269 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
270 | MTL_ENABLE_DEBUG_INFO = NO;
271 | MTL_FAST_MATH = YES;
272 | SDKROOT = iphoneos;
273 | SWIFT_COMPILATION_MODE = wholemodule;
274 | SWIFT_OPTIMIZATION_LEVEL = "-O";
275 | VALIDATE_PRODUCT = YES;
276 | };
277 | name = Release;
278 | };
279 | AF487D2625D44B8400E8B4BD /* Debug */ = {
280 | isa = XCBuildConfiguration;
281 | buildSettings = {
282 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
283 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
284 | CODE_SIGN_STYLE = Automatic;
285 | DEVELOPMENT_ASSET_PATHS = "\"StarRatingExample/Preview Content\"";
286 | DEVELOPMENT_TEAM = RB643832BT;
287 | ENABLE_PREVIEWS = YES;
288 | INFOPLIST_FILE = StarRatingExample/Info.plist;
289 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
290 | LD_RUNPATH_SEARCH_PATHS = (
291 | "$(inherited)",
292 | "@executable_path/Frameworks",
293 | );
294 | PRODUCT_BUNDLE_IDENTIFIER = com.danielkloeck.StarRatingExample;
295 | PRODUCT_NAME = "$(TARGET_NAME)";
296 | SWIFT_VERSION = 5.0;
297 | TARGETED_DEVICE_FAMILY = "1,2";
298 | };
299 | name = Debug;
300 | };
301 | AF487D2725D44B8400E8B4BD /* Release */ = {
302 | isa = XCBuildConfiguration;
303 | buildSettings = {
304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
305 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
306 | CODE_SIGN_STYLE = Automatic;
307 | DEVELOPMENT_ASSET_PATHS = "\"StarRatingExample/Preview Content\"";
308 | DEVELOPMENT_TEAM = RB643832BT;
309 | ENABLE_PREVIEWS = YES;
310 | INFOPLIST_FILE = StarRatingExample/Info.plist;
311 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
312 | LD_RUNPATH_SEARCH_PATHS = (
313 | "$(inherited)",
314 | "@executable_path/Frameworks",
315 | );
316 | PRODUCT_BUNDLE_IDENTIFIER = com.danielkloeck.StarRatingExample;
317 | PRODUCT_NAME = "$(TARGET_NAME)";
318 | SWIFT_VERSION = 5.0;
319 | TARGETED_DEVICE_FAMILY = "1,2";
320 | };
321 | name = Release;
322 | };
323 | /* End XCBuildConfiguration section */
324 |
325 | /* Begin XCConfigurationList section */
326 | AF487D1125D44B8300E8B4BD /* Build configuration list for PBXProject "StarRatingExample" */ = {
327 | isa = XCConfigurationList;
328 | buildConfigurations = (
329 | AF487D2325D44B8400E8B4BD /* Debug */,
330 | AF487D2425D44B8400E8B4BD /* Release */,
331 | );
332 | defaultConfigurationIsVisible = 0;
333 | defaultConfigurationName = Release;
334 | };
335 | AF487D2525D44B8400E8B4BD /* Build configuration list for PBXNativeTarget "StarRatingExample" */ = {
336 | isa = XCConfigurationList;
337 | buildConfigurations = (
338 | AF487D2625D44B8400E8B4BD /* Debug */,
339 | AF487D2725D44B8400E8B4BD /* Release */,
340 | );
341 | defaultConfigurationIsVisible = 0;
342 | defaultConfigurationName = Release;
343 | };
344 | /* End XCConfigurationList section */
345 |
346 | /* Begin XCRemoteSwiftPackageReference section */
347 | AF487D2925D44D0500E8B4BD /* XCRemoteSwiftPackageReference "StarRating" */ = {
348 | isa = XCRemoteSwiftPackageReference;
349 | repositoryURL = "https://github.com/dkk/StarRating";
350 | requirement = {
351 | branch = main;
352 | kind = branch;
353 | };
354 | };
355 | /* End XCRemoteSwiftPackageReference section */
356 |
357 | /* Begin XCSwiftPackageProductDependency section */
358 | AF487D2A25D44D0500E8B4BD /* StarRating */ = {
359 | isa = XCSwiftPackageProductDependency;
360 | package = AF487D2925D44D0500E8B4BD /* XCRemoteSwiftPackageReference "StarRating" */;
361 | productName = StarRating;
362 | };
363 | /* End XCSwiftPackageProductDependency section */
364 | };
365 | rootObject = AF487D0E25D44B8300E8B4BD /* Project object */;
366 | }
367 |
--------------------------------------------------------------------------------