Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LMN-27] Progress Bar SwfitUI #1716

Merged
merged 8 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions Backpack-SwiftUI/ProgressBar/Classes/BPKProgressBar.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Backpack - Skyscanner's Design System
*
* Copyright 2018-2022 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

public struct BPKProgressBar: View {
public enum Size {
case small, large

var height: BPKSpacing {
switch self {
case .small:
return BPKSpacing.md
case .large:
return BPKSpacing.base
}
}
}

let max: Int
let stepped: Bool
let size: Size
let value: Float

@Environment(\.layoutDirection) private var layoutDirection

public init(max: Int, stepped: Bool, size: Size, value: Float) {
self.max = max
self.stepped = stepped
self.size = size
self.value = value
}

private var progress: Float {
min(Swift.max(0, value), Float(max))
}

private var percentage: Float {
progress / Float(max)
}

public var body: some View {
GeometryReader { proxy in
ZStack {
Capsule()
.foregroundColor(BPKColor.surfaceHighlightColor)
progressBarShape
.foregroundColor(BPKColor.coreAccentColor)
.frame(width: width(for: proxy))
.offset(x: offset(for: proxy))
if stepped {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the stepped RTL snapshot test looks a little broken

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixeddddd

HStack {
Spacer()
ForEach(0..<max - 1, id: \.self) { _ in
Rectangle()
.foregroundColor(BPKColor.surfaceDefaultColor)
.frame(width: 2)
Spacer()
}
}
.frame(width: proxy.size.width, height: proxy.size.height)
}
}
}
.frame(height: size.height)
.animation(.easeInOut, value: value)
}

@ViewBuilder
private var progressBarShape: some View {
if stepped {
/// iOS 16 adds support for leading and trailing semantics rather than left-right
/// but XCode 15 is needed to use that feature.
/// Update API to leading and trailing when XCode 15 is used in CI
let corners: UIRectCorner = value == Float(max) ? .allCorners
: layoutDirection == .leftToRight ? [.topLeft, .bottomLeft]
: [.topRight, .bottomRight]
let cornerSize = size.height.value / 2
Rectangle()
.clipShape(
RoundedCustomCornersShape(
radius: cornerSize,
corners: corners
)
)
} else {
Capsule()
}
}

private func width(for proxy: GeometryProxy) -> CGFloat {
if stepped {
let stepWidth = proxy.size.width / CGFloat(max)
return stepWidth * CGFloat(Int(value))
}
return proxy.size.width * CGFloat(percentage)
}

private func offset(for proxy: GeometryProxy) -> CGFloat {
return -proxy.size.width / 2 + width(for: proxy) / 2
}
}

struct BPKProgressBar_Previews: PreviewProvider {
static var previews: some View {
VStack {
BPKProgressBar(max: 3, stepped: false, size: .small, value: 1.5)
BPKProgressBar(max: 3, stepped: true, size: .small, value: 1.5)
BPKProgressBar(max: 3, stepped: false, size: .large, value: 1.5)
BPKProgressBar(max: 3, stepped: true, size: .large, value: 1.5)
}
.padding()
}
}
36 changes: 36 additions & 0 deletions Backpack-SwiftUI/ProgressBar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Backpack-SwiftUI/ProgressBar

[![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/BPKProgressBar.html)
[![view on Github](https://img.shields.io/badge/Source%20code-GitHub-lightgrey)](https://github.com/Skyscanner/backpack-ios/tree/main/Backpack-SwiftUI/ProgressBar)

## Default
| Day | Night |
| --- | --- |
| <img src="https://raw.githubusercontent.com/Skyscanner/backpack-ios/main/screenshots/iPhone-swiftui_progress-bar___default_lm.png" alt="" width="375" /> |<img src="https://raw.githubusercontent.com/Skyscanner/backpack-ios/main/screenshots/iPhone-swiftui_progress-bar___default_dm.png" alt="" width="375" /> |

## Usage

### Small Progress Bar

```swift
BPKProgressBar(max: 3, stepped: false, size: .small, value: 1.5)
```

### Small Stepped Progress Bar

```swift
BPKProgressBar(max: 3, stepped: true, size: .small, value: 1.5)
```

### Large Progress Bar

```swift
BPKProgressBar(max: 3, stepped: false, size: .large, value: 1.5)
```

### Large Stepped Progress Bar

```swift
BPKProgressBar(max: 3, stepped: true, size: .large, value: 1.5)
```
52 changes: 52 additions & 0 deletions Backpack-SwiftUI/Tests/ProgressBar/BPKProgressBarTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 XCTest
import SwiftUI
@testable import Backpack_SwiftUI

class BPKProgressBarTests: XCTestCase {

func test_small_progress_bar() throws {
assertSnapshot(
BPKProgressBar(max: 3, stepped: false, size: .small, value: 1.5)
.frame(width: 200)
)
}

func test_large_progress_bar() throws {
assertSnapshot(
BPKProgressBar(max: 3, stepped: false, size: .large, value: 1.5)
.frame(width: 200)
)
}

func test_small_stepped_progress_bar() throws {
assertSnapshot(
BPKProgressBar(max: 3, stepped: true, size: .small, value: 1.5)
.frame(width: 200)
)
}

func test_large_stepped_progress_bar() throws {
assertSnapshot(
BPKProgressBar(max: 3, stepped: true, size: .large, value: 1.5)
.frame(width: 200)
)
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import SwiftUI

struct BottomSheetShape: Shape {
struct RoundedCustomCornersShape: Shape {
let radius: CGFloat
let corners: UIRectCorner

Expand All @@ -32,8 +32,8 @@ struct BottomSheetShape: Shape {
}
}

extension Shape where Self == BottomSheetShape {
static var bottomSheet: BottomSheetShape {
BottomSheetShape(radius: BPKCornerRadius.lg.value, corners: [.topLeft, .topRight])
extension Shape where Self == RoundedCustomCornersShape {
static var bottomSheet: RoundedCustomCornersShape {
RoundedCustomCornersShape(radius: BPKCornerRadius.lg.value, corners: [.topLeft, .topRight])
}
}
7 changes: 6 additions & 1 deletion Example/Backpack Screenshot/SwiftUIScreenshots.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,12 @@ class SwiftUIScreenshots: BackpackSnapshotTestCase {
saveScreenshot(component: "star-rating", scenario: "all", userInterfaceStyle: userInterfaceStyle)
tapBackButton()
}


navigate(title: "Progress bar") {
switchTab(title: "SwiftUI")
saveScreenshot(component: "progress-bar", scenario: "default", userInterfaceStyle: userInterfaceStyle)
}

navigate(title: "Section header") {
switchTab(title: "SwiftUI")
app.tables.staticTexts["Default"].tap()
Expand Down
12 changes: 12 additions & 0 deletions Example/Backpack.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
3A7D2D47214AB9F400ECBD5B /* BPKButtonsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A7D2D46214AB9F400ECBD5B /* BPKButtonsViewController.m */; };
3AA018EF215BE26600838FBB /* SpinnersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA018EE215BE26500838FBB /* SpinnersViewController.swift */; };
3AA018F4215D000700838FBB /* TextViewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA018F3215D000700838FBB /* TextViewsViewController.swift */; };
5346243D2A7437A10059A0B5 /* ProgressBarExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5346243C2A7437A10059A0B5 /* ProgressBarExampleView.swift */; };
534624382A7429080059A0B5 /* ChipGroupMultipleSelectWrapExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534624372A7429080059A0B5 /* ChipGroupMultipleSelectWrapExampleView.swift */; };
5346243A2A74292D0059A0B5 /* ChipGroupMultipleSelectRailExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534624392A74292D0059A0B5 /* ChipGroupMultipleSelectRailExampleView.swift */; };
537ED1AF282D65A300032105 /* ShadowTokensView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537ED1AE282D65A300032105 /* ShadowTokensView.swift */; };
Expand Down Expand Up @@ -226,6 +227,7 @@
3AA018F3215D000700838FBB /* TextViewsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewsViewController.swift; sourceTree = "<group>"; };
3DDD5D1DB6A77816BAA9481F /* Pods-Backpack-Native.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backpack-Native.release.xcconfig"; path = "Pods/Target Support Files/Pods-Backpack-Native/Pods-Backpack-Native.release.xcconfig"; sourceTree = "<group>"; };
455FB6E0E01310446E946427 /* Backpack.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Backpack.podspec; path = ../Backpack.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
5346243C2A7437A10059A0B5 /* ProgressBarExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarExampleView.swift; sourceTree = "<group>"; };
534624372A7429080059A0B5 /* ChipGroupMultipleSelectWrapExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChipGroupMultipleSelectWrapExampleView.swift; sourceTree = "<group>"; };
534624392A74292D0059A0B5 /* ChipGroupMultipleSelectRailExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChipGroupMultipleSelectRailExampleView.swift; sourceTree = "<group>"; };
537ED1AE282D65A300032105 /* ShadowTokensView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowTokensView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -481,6 +483,14 @@
path = Select;
sourceTree = "<group>";
};
5346243E2A7437AF0059A0B5 /* ProgressBar */ = {
isa = PBXGroup;
children = (
5346243C2A7437A10059A0B5 /* ProgressBarExampleView.swift */,
);
path = ProgressBar;
sourceTree = "<group>";
};
5346243B2A7433040059A0B5 /* MultiSelect */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -849,6 +859,7 @@
793EC5502836139A00D627F6 /* Components */ = {
isa = PBXGroup;
children = (
5346243E2A7437AF0059A0B5 /* ProgressBar */,
A17006532A5BBEE900134458 /* BottomSheet */,
A1B2DBFF2A43077E00DE6980 /* ChipGroup */,
7904384229F11DC7006CD8E0 /* Slider */,
Expand Down Expand Up @@ -1692,6 +1703,7 @@
5390DB5F29098D7300F0F790 /* SpacingTokensViewController.swift in Sources */,
53C6621F29EA0DAB00BF1A62 /* StarRatingPlayground.swift in Sources */,
8071379C25AF7974009869D1 /* BottomSheetPersistentViewController.swift in Sources */,
5346243D2A7437A10059A0B5 /* ProgressBarExampleView.swift in Sources */,
53C6622629EA0DAB00BF1A62 /* ChipExampleView.swift in Sources */,
D261F26C22C0E6B600A4A476 /* TappableLinkLabelsViewController.swift in Sources */,
764D003A237DE4E300FE60AC /* SnackbarSelectorViewController.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
/*
* Backpack - Skyscanner's Design System
*
* Copyright © 2023 Skyscanner Ltd. All rights reserved.
*
* 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
import Backpack_SwiftUI

struct ProgressBarExampleView: View {
@State var progress: Float = 1.5

var body: some View {
VStack(alignment: .leading) {
BPKText("Small", style: .heading3)
BPKProgressBar(max: 3, stepped: false, size: .small, value: progress)
BPKText("Steped", style: .heading5)
BPKProgressBar(max: 3, stepped: true, size: .small, value: progress)
BPKText("Large", style: .heading3)
BPKProgressBar(max: 3, stepped: false, size: .large, value: progress)
BPKText("Steped", style: .heading5)
BPKProgressBar(max: 3, stepped: true, size: .large, value: progress)
Spacer()
BPKSlider(value: $progress, sliderBounds: 0...3, step: 0.2)
.padding()
}
.padding()
}
}

struct ProgressBarExampleView_Previews: PreviewProvider {
static var previews: some View {
ProgressBarExampleView()
}
}
14 changes: 11 additions & 3 deletions Example/Backpack/Utils/FeatureStories/ComponentCells.swift
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,18 @@ extension ComponentCellsProvider {
)
}
private func progressBar() -> CellDataSource {
PresentableCellDataSource(
ComponentCellDataSource(
title: "Progress bar",
storyboard: .named("ProgressBar", on: "ProgressBarViewController"),
showPresentable: show(presentable:)
tabs: [
.uikit(presentable: loadStoryboard(
name: "ProgressBar",
identifier: "ProgressBarViewController"
)),
.swiftui(presentable: CustomPresentable(generateViewController: {
ContentUIHostingController(ProgressBarExampleView())
}))
],
showChildren: { showComponent(title: "Progress bar", tabs: $0) }
)
}
private func ratings() -> CellDataSource {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading