Skip to content

Commit 3cbbfb2

Browse files
author
Trent Guillory
committed
Code cleanup, configuration, and readme.
1 parent baf5737 commit 3cbbfb2

File tree

5 files changed

+109
-50
lines changed

5 files changed

+109
-50
lines changed
Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1-
//
2-
// AppDelegate.swift
3-
// ActionButtonDemo
4-
//
5-
// Created by Trent Guillory on 9/10/21.
6-
//
7-
81
import UIKit
92

103
@main
114
class AppDelegate: UIResponder, UIApplicationDelegate {
125

13-
14-
15-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
6+
func application(
7+
_ application: UIApplication,
8+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
9+
-> Bool {
1610
// Override point for customization after application launch.
1711
return true
1812
}
1913

2014
// MARK: UISceneSession Lifecycle
2115

22-
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
16+
func application(
17+
_ application: UIApplication,
18+
configurationForConnecting connectingSceneSession: UISceneSession,
19+
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
2320
// Called when a new scene session is being created.
2421
// Use this method to select a configuration to create the new scene with.
25-
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
22+
return UISceneConfiguration(
23+
name: "Default Configuration",
24+
sessionRole: connectingSceneSession.role)
2625
}
2726

28-
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
27+
func application(
28+
_ application: UIApplication,
29+
didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
2930
// Called when the user discards a scene session.
3031
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
3132
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
3233
}
33-
34-
3534
}
36-

ActionButtonDemo/Sources/ContentView.swift

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,33 @@ import SwiftUI
44
// MARK: - ContentView
55

66
struct ContentView: View {
7-
8-
@State var actionButtonState: ActionButtonState = .enabled(.init(title: "Load Something", systemImage: "bolt"))
9-
7+
8+
// MARK: Internal
9+
10+
@State var actionButtonState: ActionButtonState =
11+
.enabled(.init(title: "Load Something", systemImage: "bolt"))
12+
1013
var body: some View {
1114

1215
VStack {
1316

1417
Text("Just a little button.")
1518
ActionButton(state: $actionButtonState, onTap: {
16-
19+
1720
loadSomething()
18-
})
19-
.frame(maxWidth: 250)
21+
}, backgroundColor: .red)
22+
.frame(maxWidth: 250)
2023
}
2124
}
22-
25+
26+
// MARK: Private
27+
2328
private func loadSomething() {
24-
29+
2530
actionButtonState = .loading(.init(title: "Loading", systemImage: "bolt"))
26-
31+
2732
DispatchQueue.main.asyncAfter(deadline: .now() + 1.25) {
28-
33+
2934
actionButtonState = .disabled(.init(title: "Loaded", systemImage: "checkmark"))
3035
}
3136
}

ActionButtonDemo/Sources/SceneDelegate.swift

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1-
//
2-
// SceneDelegate.swift
3-
// ActionButtonDemo
4-
//
5-
// Created by Trent Guillory on 9/10/21.
6-
//
7-
8-
import UIKit
91
import SwiftUI
2+
import UIKit
103

114
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
125

136
var window: UIWindow?
147

15-
16-
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
8+
func scene(
9+
_ scene: UIScene,
10+
willConnectTo session: UISceneSession,
11+
options connectionOptions: UIScene.ConnectionOptions) {
1712
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
1813
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
1914
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
@@ -57,7 +52,4 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
5752
// Use this method to save data, release shared resources, and store enough scene-specific state information
5853
// to restore the scene back to its current state.
5954
}
60-
61-
6255
}
63-

README.md

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,54 @@
11
# ActionButton
22

3-
A description of this package.
3+
`Button` alternative providing built-in states for `.enabled`, `.disabled`, and `.loading`.
4+
5+
## How to Use
6+
7+
1. Import `ActionButton`
8+
2. Create a state or binding variable of type `ActionState`. This will drive the content of `ActionButton`
9+
3. Add `ActionButton` to your view and configure it.
10+
11+
Below is a an example of the code used in the video demo above. So how do we animate between states?
12+
13+
```swift
14+
@State var actionButtonState: ActionButtonState =
15+
.enabled(.init(title: "Load Something", systemImage: "bolt"))
16+
17+
var body: some View {
18+
19+
VStack {
20+
21+
Text("Just a little button.")
22+
ActionButton(state: $actionButtonState, onTap: {
23+
24+
loadSomething()
25+
}, backgroundColor: .red)
26+
.frame(maxWidth: 250)
27+
}
28+
}
29+
```
30+
**Animating between states.**
31+
32+
Animating between states is handled automatically. Take a look at our `loadSomething()` function.
33+
34+
```swift
35+
private func loadSomething() {
36+
37+
actionButtonState = .loading(.init(title: "Loading", systemImage: "bolt"))
38+
39+
DispatchQueue.main.asyncAfter(deadline: .now() + 1.25) {
40+
41+
actionButtonState = .disabled(.init(title: "Loaded", systemImage: "checkmark"))
42+
}
43+
}
44+
```
45+
In our example, we use a delay to simply change the state variable `actionButtonState`. The view responds accordingly. k
46+
47+
## Configuration
48+
49+
`ActionButton` takes in a handful of parameters in its initialization.
50+
51+
- `state`: `ActionButtonState` - This is an enum (enabled, loading, disabled) with an associated value of type `ActionButtonModel` which allows you to set the SFSymbol and text of the button.
52+
- `onTap`: `(() -> Void)` - A callback to handle any taps. This is required since `ActionButton` handles its own tap animation. Your own `onTapGesture` won't work here.
53+
- `backgroundColor`: `Color` - the background color for the button during its `enabled` state.
54+
- `foregroundColor`: `Color` - the foreground color for the button during all states.

Sources/ActionButton.swift

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@ public struct ActionButton: View {
88

99
// MARK: Lifecycle
1010

11-
public init(state: Binding<ActionButtonState>, onTap: @escaping TapHandler) {
11+
public init(
12+
state: Binding<ActionButtonState>,
13+
onTap: @escaping TapHandler,
14+
backgroundColor: Color,
15+
foregroundColor: Color = .white) {
1216

1317
_state = state
18+
self.foregroundColor = foregroundColor
19+
self.backgroundColor = backgroundColor
1420
tapHandler = onTap
1521
}
1622

@@ -37,13 +43,13 @@ public struct ActionButton: View {
3743
Text(state.title)
3844
}
3945
.font(.system(size: 14, weight: .medium))
40-
.foregroundColor(.white)
46+
.foregroundColor(foregroundColor)
4147

4248
Spacer()
4349
}
4450
.frame(height: 22)
4551
.padding(8)
46-
.background(backgroundColor)
52+
.background(background)
4753
.animation(.fastFade, value: state.enabled)
4854
.cornerRadius(8)
4955
.scaleEffect(scale)
@@ -62,28 +68,35 @@ public struct ActionButton: View {
6268
.onEnded { _ in
6369

6470
self.isPressing = false
65-
tapHandler()
71+
72+
switch state {
73+
74+
case .enabled: tapHandler()
75+
default: break
76+
}
6677
}
6778
}
68-
79+
6980
var scale: CGFloat {
70-
81+
7182
switch state {
72-
83+
7384
case .enabled: return isPressing ? 0.95 : 1
7485
default: return 1
7586
}
7687
}
7788

78-
var backgroundColor: Color {
89+
var background: Color {
7990

80-
state.enabled ? Color.red : Color.gray
91+
state.enabled ? backgroundColor : Color.gray
8192
}
8293

8394
// MARK: Private
8495

8596
@State private var isPressing = false
8697
private var tapHandler: TapHandler
98+
private var backgroundColor: Color
99+
private var foregroundColor: Color
87100
}
88101

89102
// MARK: - ActionButton_Previews
@@ -94,6 +107,6 @@ struct ActionButton_Previews: PreviewProvider {
94107

95108
ActionButton(state: .constant(.enabled(.init(
96109
title: "New Draft",
97-
systemImage: "plus.circle"))), onTap: {})
110+
systemImage: "plus.circle"))), onTap: {}, backgroundColor: .red)
98111
}
99112
}

0 commit comments

Comments
 (0)