Skip to content

Commit dcbb90f

Browse files
committed
Adds a demo project for writing sample code
1 parent 8066a07 commit dcbb90f

12 files changed

+387
-7
lines changed

Diff for: Package.resolved

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"pins" : [
3+
{
4+
"identity" : "swiftuibackports",
5+
"kind" : "remoteSourceControl",
6+
"location" : "https://github.com/shaps80/SwiftUIBackports",
7+
"state" : {
8+
"revision" : "252d01385a0730cc604310373ac934a64f1d4b1a",
9+
"version" : "1.8.2"
10+
}
11+
}
12+
],
13+
"version" : 2
14+
}

Diff for: Package.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ let package = Package(
1515
targets: ["SwiftHelp"]
1616
),
1717
],
18+
dependencies: [
19+
.package(url: "https://github.com/shaps80/SwiftUIBackports", .upToNextMajor(from: "1.0.0"))
20+
],
1821
targets: [
19-
.target(name: "SwiftHelp"),
22+
.target(
23+
name: "SwiftHelp",
24+
dependencies: ["SwiftUIBackports"]
25+
),
2026
]
2127
)

Diff for: README.md

+16
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,19 @@
55
# SwiftHelp
66

77
A SwiftUI library for building interactive help systems in your apps.
8+
9+
## Sponsor
10+
11+
Building useful libraries like these, takes time away from my family. I build these tools in my spare time because I feel its important to give back to the community. Please consider [Sponsoring](https://github.com/sponsors/shaps80) me as it helps keep me working on useful libraries like these 😬
12+
13+
You can also give me a follow and a 'thanks' anytime.
14+
15+
[![Twitter](https://img.shields.io/badge/Twitter-@shaps-4AC71B)](http://twitter.com/shaps)
16+
17+
## Installation
18+
19+
You can install manually (by copying the files in the `Sources` directory) or using Swift Package Manager (**preferred**)
20+
21+
To install using Swift Package Manager, add this to the `dependencies` section of your `Package.swift` file:
22+
23+
`.package(url: "https://github.com/shaps80/SwiftHelp.git", .upToNextMinor(from: "1.0.0"))`

Diff for: Sources/SwiftHelp/Help.swift

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import SwiftUI
2+
3+
public protocol Help: Identifiable where ID == String {
4+
associatedtype Body: View
5+
var title: String { get }
6+
@ViewBuilder var body: Body { get }
7+
}
8+
9+
public struct AnyHelp: Help {
10+
public var id: String
11+
public var title: String
12+
public var body: AnyView
13+
14+
public init<H: Help>(_ help: H) {
15+
self.id = String(describing: help.id)
16+
self.title = help.title
17+
self.body = { .init(help.body) }()
18+
}
19+
}
20+
21+
private struct HelpEnvironmentKey: EnvironmentKey {
22+
static var defaultValue: Binding<AnyHelp?> = .constant(nil)
23+
}
24+
25+
internal extension EnvironmentValues {
26+
var help: Binding<AnyHelp?> {
27+
get { self[HelpEnvironmentKey.self] }
28+
set { self[HelpEnvironmentKey.self] = newValue }
29+
}
30+
}

Diff for: Sources/SwiftHelp/HelpValues.swift

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import SwiftUI
2+
3+
public protocol HelpKey {
4+
associatedtype Body: Help
5+
static var help: Body { get }
6+
}
7+
8+
public struct HelpValues {
9+
public init() { }
10+
public subscript<K: HelpKey>(key: K.Type) -> some Help { K.help }
11+
}

Diff for: Sources/SwiftHelp/HelpVisibility.swift

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import SwiftUI
2+
import SwiftUIBackports
3+
4+
extension View {
5+
func help(_ visibility: Backport<Any>.Visibility) -> some View {
6+
modifier(HelpVisibilityModifier(visibility: visibility))
7+
}
8+
}
9+
10+
private struct HelpVisibilityModifier: ViewModifier {
11+
@Environment(\.helpPresentationStyle) private var presentation
12+
@State private var help: AnyHelp?
13+
var visibility: Backport<Any>.Visibility
14+
15+
func body(content: Content) -> some View {
16+
content
17+
.environment(\.help, $help)
18+
.environment(\.helpVisibility, visibility)
19+
.sheet(item: $help) { help in
20+
presentation.makeBody(configuration: .init(help: help, label: help.body))
21+
}
22+
}
23+
}
24+
25+
private struct HelpVisibilityEnvironmentKey: EnvironmentKey {
26+
static var defaultValue: Backport<Any>.Visibility = .hidden
27+
}
28+
29+
public extension EnvironmentValues {
30+
var helpVisibility: Backport<Any>.Visibility {
31+
get { self[HelpVisibilityEnvironmentKey.self] }
32+
set { self[HelpVisibilityEnvironmentKey.self] = newValue }
33+
}
34+
}

Diff for: Sources/SwiftHelp/Presentation/HelpNavigation.swift

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import SwiftUI
2+
3+
extension NavigationLink where Label == Text, Destination == AnyView {
4+
public init<S: StringProtocol, H: Help>(_ title: S, help keyPath: KeyPath<HelpValues, H>) {
5+
let help = HelpValues()[keyPath: keyPath]
6+
self.init(title) {
7+
AnyView(
8+
HelpNavigationView(help: help)
9+
.helpPresentationStyle(.plain)
10+
)
11+
}
12+
}
13+
14+
public init<H: Help>(_ titleKey: LocalizedStringKey, help keyPath: KeyPath<HelpValues, H>) {
15+
let help = HelpValues()[keyPath: keyPath]
16+
self.init(titleKey) {
17+
AnyView(
18+
HelpNavigationView(help: help)
19+
.helpPresentationStyle(.plain)
20+
)
21+
}
22+
}
23+
}
24+
25+
extension NavigationLink where Destination == AnyView {
26+
public init<H: Help>(help keyPath: KeyPath<HelpValues, H>, @ViewBuilder label: () -> Label) {
27+
let help = HelpValues()[keyPath: keyPath]
28+
self.init {
29+
AnyView(
30+
HelpNavigationView(help: help)
31+
.helpPresentationStyle(.plain)
32+
)
33+
} label: {
34+
label()
35+
}
36+
}
37+
}
38+
39+
extension NavigationLink where Label == Text, Destination == AnyView {
40+
public init<H: Help>(help keyPath: KeyPath<HelpValues, H>) {
41+
let help = HelpValues()[keyPath: keyPath]
42+
self.init {
43+
AnyView(
44+
HelpNavigationView(help: help)
45+
.helpPresentationStyle(.plain)
46+
)
47+
} label: {
48+
Text(help.title)
49+
}
50+
}
51+
}
52+
53+
private struct HelpNavigationView<H: Help>: View {
54+
@Environment(\.helpPresentationStyle) private var style
55+
let help: H
56+
var body: some View {
57+
style.makeBody(configuration: .init(
58+
help: help,
59+
label: help.body
60+
))
61+
}
62+
}

Diff for: Sources/SwiftHelp/Presentation/HelpSheet.swift

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import SwiftUI
2+
3+
extension View {
4+
func help<H: Help>(_ keyPath: KeyPath<HelpValues, H>) -> some View {
5+
modifier(HelpSheetModifier(help: HelpValues()[keyPath: keyPath]))
6+
}
7+
}
8+
9+
private struct HelpSheetModifier<H: Help>: ViewModifier {
10+
@Environment(\.helpVisibility) private var visibility
11+
@Environment(\.helpElementStyle) private var style
12+
@Environment(\.help) private var selection
13+
14+
let help: H
15+
func body(content: Content) -> some View {
16+
if visibility != .hidden {
17+
Button {
18+
selection.wrappedValue = .init(help)
19+
} label: {
20+
style.makeBody(configuration: .init(
21+
help: help,
22+
label: content
23+
))
24+
}
25+
.buttonStyle(.plain)
26+
} else {
27+
content
28+
}
29+
}
30+
}

Diff for: Sources/SwiftHelp/Styles/HelpElementStyle.swift

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import SwiftUI
2+
import SwiftUIBackports
3+
4+
public protocol HelpElementStyle {
5+
associatedtype Body: View
6+
typealias Configuration = HelpElementConfiguration
7+
@ViewBuilder func makeBody(configuration: Configuration) -> Body
8+
}
9+
10+
public struct AnyHelpElementStyle: HelpElementStyle {
11+
var label: (Configuration) -> AnyView
12+
init<S: HelpElementStyle>(_ style: S) {
13+
label = { AnyView(style.makeBody(configuration: $0)) }
14+
}
15+
16+
public func makeBody(configuration: Configuration) -> some View {
17+
label(configuration)
18+
}
19+
}
20+
21+
public struct HelpElementConfiguration {
22+
public var help: AnyHelp
23+
public var label: AnyView
24+
25+
init<H: Help, Label: View>(help: H, label: Label) {
26+
self.help = .init(help)
27+
self.label = .init(label)
28+
}
29+
30+
init<H: Help, Label: View>(help: H, @ViewBuilder label: () -> Label) {
31+
self.help = .init(help)
32+
self.label = .init(label())
33+
}
34+
}
35+
36+
public struct DefaultHelpElementStyle: HelpElementStyle {
37+
public init() { }
38+
public func makeBody(configuration: Configuration) -> some View {
39+
configuration.label
40+
.backport.overlay {
41+
RoundedRectangle(cornerRadius: 13, style: .continuous)
42+
.stroke(Color.yellow, lineWidth: 2)
43+
}
44+
}
45+
}
46+
47+
public extension HelpElementStyle where Self == DefaultHelpElementStyle {
48+
static var `default`: Self { .init() }
49+
}
50+
51+
private struct HelpElementStyleEnvironmentKey: EnvironmentKey {
52+
static let defaultValue = AnyHelpElementStyle(.default)
53+
}
54+
55+
public extension EnvironmentValues {
56+
var helpElementStyle: AnyHelpElementStyle {
57+
get { self[HelpElementStyleEnvironmentKey.self] }
58+
set { self[HelpElementStyleEnvironmentKey.self] = newValue }
59+
}
60+
}
61+
62+
public extension View {
63+
func helpElementStyle<S: HelpElementStyle>(_ style: S) -> some View {
64+
environment(\.helpElementStyle, AnyHelpElementStyle(style))
65+
}
66+
}

Diff for: Sources/SwiftHelp/Styles/HelpPresentationStyle.swift

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import SwiftUI
2+
import SwiftUIBackports
3+
4+
public protocol HelpPresentationStyle {
5+
associatedtype Body: View
6+
typealias Configuration = HelpPresentationConfiguration
7+
@ViewBuilder func makeBody(configuration: Configuration) -> Body
8+
}
9+
10+
public struct AnyHelpPresentationStyle: HelpPresentationStyle {
11+
var label: (Configuration) -> AnyView
12+
init<S: HelpPresentationStyle>(_ style: S) {
13+
label = { AnyView(style.makeBody(configuration: $0)) }
14+
}
15+
16+
public func makeBody(configuration: Configuration) -> some View {
17+
label(configuration)
18+
}
19+
}
20+
21+
public struct HelpPresentationConfiguration {
22+
public let help: AnyHelp
23+
public let label: AnyView
24+
25+
init<H: Help, Label: View>(help: H, label: Label) {
26+
self.help = .init(help)
27+
self.label = .init(label)
28+
}
29+
30+
init<H: Help, Label: View>(help: H, @ViewBuilder label: () -> Label) {
31+
self.help = .init(help)
32+
self.label = .init(label())
33+
}
34+
}
35+
36+
public struct SheetHelpPresentationStyle: HelpPresentationStyle {
37+
struct Label: View {
38+
@State private var show: Bool = false
39+
@Environment(\.presentationMode) private var presentation
40+
41+
let help: AnyHelp
42+
let content: AnyView
43+
44+
var body: some View {
45+
NavigationView {
46+
help.body
47+
.backport.navigationTitle(help.title)
48+
.backport.toolbar {
49+
Button {
50+
presentation.wrappedValue.dismiss()
51+
} label: {
52+
Text("Close")
53+
}
54+
}
55+
}
56+
}
57+
}
58+
59+
public init() { }
60+
public func makeBody(configuration: Configuration) -> some View {
61+
Label(help: configuration.help, content: configuration.label)
62+
}
63+
}
64+
65+
public extension HelpPresentationStyle where Self == SheetHelpPresentationStyle {
66+
static var sheet: Self { .init() }
67+
}
68+
69+
public struct PlainHelpPresentationStyle: HelpPresentationStyle {
70+
public init() { }
71+
public func makeBody(configuration: Configuration) -> some View {
72+
configuration.label
73+
.backport.navigationTitle(configuration.help.title)
74+
}
75+
}
76+
77+
public extension HelpPresentationStyle where Self == PlainHelpPresentationStyle {
78+
static var plain: Self { .init() }
79+
}
80+
81+
private struct HelpPresentationStyleEnvironmentKey: EnvironmentKey {
82+
static let defaultValue = AnyHelpPresentationStyle(.sheet)
83+
}
84+
85+
public extension EnvironmentValues {
86+
var helpPresentationStyle: AnyHelpPresentationStyle {
87+
get { self[HelpPresentationStyleEnvironmentKey.self] }
88+
set { self[HelpPresentationStyleEnvironmentKey.self] = newValue }
89+
}
90+
}
91+
92+
public extension View {
93+
func helpPresentationStyle<S: HelpPresentationStyle>(_ style: S) -> some View {
94+
environment(\.helpPresentationStyle, AnyHelpPresentationStyle(style))
95+
}
96+
}

Diff for: Sources/SwiftHelp/SwiftHelp.swift

-6
This file was deleted.

0 commit comments

Comments
 (0)