-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[KOA-5084] BPKStarRating SwiftUI (#1632)
* initial setup * Star Rating component * addressing pr comments * added hotel star ratings to readme
- Loading branch information
Showing
41 changed files
with
833 additions
and
152 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
Backpack-SwiftUI/StarRating/Classes/BPKHotelStarRating.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,62 @@ | ||
/* | ||
* Backpack - Skyscanner's Design System | ||
* | ||
* Copyright 2018 Skyscanner Ltd | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import SwiftUI | ||
|
||
/// A view that displays a star rating based on a rating value. | ||
public struct BPKHotelStarRating: View { | ||
@Binding private var rating: Int | ||
private let size: BPKStarRatingSize | ||
|
||
/// Initializes a new `BPKHotelStarRating` instance. | ||
/// - Parameters: | ||
/// - rating: A binding to the current rating value. | ||
/// Represents the number of stars to display. | ||
/// - size: The size of the star rating, defaults to `.small`. | ||
public init( | ||
rating: Binding<Int>, | ||
size: BPKStarRatingSize = .small | ||
) { | ||
self._rating = rating | ||
self.size = size | ||
} | ||
|
||
public var body: some View { | ||
HStack(spacing: 0) { | ||
ForEach(0..<rating, id: \.self) { index in | ||
BPKStarView(type: .full, size: size.starSize) | ||
.accessibilityHidden(true) | ||
} | ||
} | ||
.accessibilityElement() | ||
.accessibilityValue(Text(String(rating))) | ||
} | ||
} | ||
|
||
struct BPKHotelStarRating_Previews: PreviewProvider { | ||
static var previews: some View { | ||
VStack(alignment: .leading) { | ||
BPKText("Small", style: .heading3) | ||
BPKHotelStarRating(rating: .constant(3)) | ||
BPKHotelStarRating(rating: .constant(4)) | ||
BPKText("Large", style: .heading3) | ||
BPKHotelStarRating(rating: .constant(3), size: .large) | ||
BPKHotelStarRating(rating: .constant(4), size: .large) | ||
} | ||
} | ||
} |
125 changes: 125 additions & 0 deletions
125
Backpack-SwiftUI/StarRating/Classes/BPKStarRating.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,125 @@ | ||
/* | ||
* Backpack - Skyscanner's Design System | ||
* | ||
* Copyright 2018 Skyscanner Ltd | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import SwiftUI | ||
|
||
/// A view that displays a star rating based on a rating value. | ||
public struct BPKStarRating: View { | ||
/// An enum representing the rounding method to use. | ||
/// - down: Rounds down to the nearest half star. | ||
/// - up: Rounds up to the nearest half star. | ||
/// - nearest: Rounds to the nearest half star. | ||
public enum Rounding { | ||
case down, up, nearest | ||
|
||
func roundedRating(_ rating: Float) -> Float { | ||
switch self { | ||
case .down: return floor(rating * 2) / 2 | ||
case .up: return ceil(rating * 2) / 2 | ||
case .nearest: return round(rating * 2) / 2 | ||
} | ||
} | ||
} | ||
|
||
@Binding private var rating: Float | ||
private let maxRating: Int | ||
private let size: BPKStarRatingSize | ||
private let rounding: Rounding | ||
private let onRatingChanged: ((Float) -> Void)? | ||
|
||
/// Initializes a new `BPKStarRating` instance. | ||
/// - Parameters: | ||
/// - rating: A binding to the current rating value. | ||
/// - maxRating: The maximum rating value allowed. | ||
/// Represents the number of stars to display. | ||
/// - size: The size of the star rating, defaults to `.small`. | ||
/// - rounding: The rounding method to use, defaults to `.down`. | ||
/// - onRatingChanged: A callback that is called when the user taps on a star. | ||
public init( | ||
rating: Binding<Float>, | ||
maxRating: Int, | ||
size: BPKStarRatingSize = .small, | ||
rounding: Rounding = .down, | ||
onRatingChanged: ((Float) -> Void)? = nil | ||
) { | ||
self._rating = rating | ||
self.maxRating = maxRating | ||
self.size = size | ||
self.rounding = rounding | ||
self.onRatingChanged = onRatingChanged | ||
} | ||
|
||
public var body: some View { | ||
HStack(spacing: 0) { | ||
ForEach(0..<maxRating, id: \.self) { index in | ||
BPKStarView(type: starType(for: index), size: size.starSize) | ||
.onTapGesture { | ||
onRatingChanged?(Float(index + 1)) | ||
} .accessibilityHidden(true) | ||
} | ||
} | ||
.accessibilityElement() | ||
.accessibilityValue(Text(String(coercedRating))) | ||
.if(onRatingChanged != nil) { view in | ||
view.accessibilityAdjustableAction { direction in | ||
switch direction { | ||
case .increment: increment() | ||
case .decrement: decrement() | ||
@unknown default: break | ||
} | ||
} | ||
} | ||
} | ||
|
||
private func increment() { | ||
onRatingChanged?(Float(min(maxRating, Int(coercedRating + 1)))) | ||
} | ||
|
||
private func decrement() { | ||
onRatingChanged?(Float(max(0, Int(coercedRating - 1)))) | ||
} | ||
|
||
private func starType(for index: Int) -> BPKStarView.StarType { | ||
let clamped = max(0, min(coercedRating - Float(index), 1)) | ||
switch clamped { | ||
case 0..<0.5: return .empty | ||
case 0.5..<1: return .half | ||
default: return .full | ||
} | ||
} | ||
|
||
private var coercedRating: Float { | ||
let coercedRating = max(0, min(Float(maxRating), rating)) | ||
return rounding.roundedRating(coercedRating) | ||
} | ||
} | ||
|
||
struct BPKStarRating_Previews: PreviewProvider { | ||
static var previews: some View { | ||
VStack(alignment: .leading) { | ||
BPKText("Small", style: .heading3) | ||
BPKStarRating(rating: .constant(3), maxRating: 5) | ||
BPKStarRating(rating: .constant(3.5), maxRating: 5) | ||
BPKStarRating(rating: .constant(4), maxRating: 5) | ||
BPKText("Large", style: .heading3) | ||
BPKStarRating(rating: .constant(3), maxRating: 5, size: .large) | ||
BPKStarRating(rating: .constant(3.5), maxRating: 5, size: .large) | ||
BPKStarRating(rating: .constant(4), maxRating: 5, size: .large) | ||
} | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
Backpack-SwiftUI/StarRating/Classes/BPKStarRatingSize.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,29 @@ | ||
/* | ||
* Backpack - Skyscanner's Design System | ||
* | ||
* Copyright 2018 Skyscanner Ltd | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
/// An enum representing the size of the star rating. | ||
public enum BPKStarRatingSize { | ||
case small, large | ||
|
||
var starSize: BPKIcon.Size { | ||
switch self { | ||
case .small: return .small | ||
case .large: return .large | ||
} | ||
} | ||
} |
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,68 @@ | ||
/* | ||
* Backpack - Skyscanner's Design System | ||
* | ||
* Copyright 2018 Skyscanner Ltd | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import SwiftUI | ||
|
||
struct BPKStarView: View { | ||
enum StarType { | ||
case empty, half, full | ||
|
||
var iconName: BPKIcon { | ||
switch self { | ||
case .empty: return .starOutline | ||
case .half: return .starHalf | ||
case .full: return .star | ||
} | ||
} | ||
|
||
var color: BPKColor { | ||
switch self { | ||
case .empty: return .textDisabledColor | ||
case .half: return .statusWarningSpotColor | ||
case .full: return .statusWarningSpotColor | ||
} | ||
} | ||
} | ||
|
||
let type: StarType | ||
let size: BPKIcon.Size | ||
|
||
var body: some View { | ||
BPKIconView(type.iconName, size: size) | ||
.foregroundColor(type.color) | ||
} | ||
} | ||
|
||
struct BPKStarView_Previews: PreviewProvider { | ||
static var previews: some View { | ||
VStack{ | ||
HStack { | ||
BPKText("Large", style: .heading3) | ||
BPKStarView(type: .empty, size: .large) | ||
BPKStarView(type: .half, size: .large) | ||
BPKStarView(type: .full, size: .large) | ||
} | ||
HStack { | ||
BPKText("Small", style: .heading3) | ||
BPKStarView(type: .empty, size: .small) | ||
BPKStarView(type: .half, size: .small) | ||
BPKStarView(type: .full, size: .small) | ||
} | ||
} | ||
} | ||
} |
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,52 @@ | ||
# Backpack-SwiftUI/StarRating | ||
|
||
[![Cocoapods](https://img.shields.io/cocoapods/v/Backpack-SwiftUI.svg?style=flat)](hhttps://cocoapods.org/pods/Backpack-SwiftUI) | ||
[![class reference](https://img.shields.io/badge/Class%20reference-iOS-blue)](https://backpack.github.io/ios/versions/latest/swiftui/Structs/BPKStarRating.html) | ||
[![view on Github](https://img.shields.io/badge/Source%20code-GitHub-lightgrey)](https://github.com/Skyscanner/backpack-ios/tree/main/Backpack-SwiftUI/StarRating) | ||
|
||
| Day | Night | | ||
| --- | --- | | ||
| <img src="https://raw.githubusercontent.com/Skyscanner/backpack-ios/main/screenshots/iPhone-swiftui_star-rating___all_lm.png" alt="" width="375" /> |<img src="https://raw.githubusercontent.com/Skyscanner/backpack-ios/main/screenshots/iPhone-swiftui_star-rating___all_dm.png" alt="" width="375" /> | | ||
|
||
## Usage | ||
|
||
To use the star rating component in your code you will need to bind the `rating` property and set the `maxRating` property. The `maxRating` is the maximum number of stars that can be displayed. | ||
|
||
### Basic star rating | ||
|
||
```swift | ||
@State var rating: Float = 3.5 | ||
BPKStarRating(rating: $rating, maxRating: 5) | ||
``` | ||
|
||
### Interactive star rating | ||
|
||
```swift | ||
@State var rating: Float = 3.5 | ||
BPKStarRating(rating: $rating, maxRating: 5) { selectedRating in | ||
rating = selectedRating | ||
} | ||
``` | ||
|
||
### Hotel star rating | ||
|
||
```swift | ||
@State var hotelRating: Int = 3 | ||
BPKHotelStarRating(rating: $hotelRating) | ||
``` | ||
|
||
### Star rating with Large size | ||
|
||
```swift | ||
@State var rating: Float = 3.5 | ||
BPKStarRating(rating: $rating, maxRating: 5, size: .large) | ||
``` | ||
|
||
### Customise Rating Rounding | ||
|
||
Rounding can be customised by setting the `rounding` property. The default is `.down`. | ||
|
||
```swift | ||
@State var rating: Float = 3.5 | ||
BPKStarRating(rating: $rating, maxRating: 5, rounding: .down) | ||
``` |
Oops, something went wrong.