Created
February 3, 2022 16:33
-
-
Save ericlewis/9c57f884f39e844c6df03da85473692b to your computer and use it in GitHub Desktop.
Using enums as Views. Just copy and paste into an App Playground.
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
import SwiftUI | |
struct Application: Identifiable { | |
enum Icon { | |
case url(URL) | |
case systemImage(String) | |
} | |
let id: UUID = .init() | |
let name: String | |
let icon: Icon | |
let color: Color | |
let builds: [Build] | |
} | |
extension Application: View { | |
var body: some View { | |
Label { | |
Text(name) | |
} icon: { | |
self.labelIcon | |
} | |
.foregroundStyle(color) | |
} | |
@ViewBuilder | |
var labelIcon: some View { | |
switch icon { | |
case let .url(url): | |
AsyncImage(url: url) | |
case let .systemImage(name): | |
Image(systemName: name) | |
} | |
} | |
} | |
struct Build: Identifiable { | |
enum Status { | |
case waiting | |
case running | |
case success | |
case failure | |
} | |
let id: UUID = .init() | |
let createdAt: Date = .now | |
let status: Status | |
} | |
extension Build: View { | |
var body: some View { | |
Text(id.uuidString.dropLast(28)) | |
} | |
} | |
extension Build.Status: View { | |
var body: some View { | |
Label(title: { Text(self.title) }, icon: { self.icon }) | |
.foregroundStyle(color) | |
} | |
var title: LocalizedStringKey { | |
switch self { | |
case .waiting: | |
return "Waiting for runner" | |
case .running: | |
return "Running..." | |
case .success: | |
return "Build Succeded" | |
case .failure: | |
return "Build Failed" | |
} | |
} | |
@ViewBuilder | |
var icon: some View { | |
switch self { | |
case .waiting: | |
Image(systemName: "pause") | |
case .running: | |
Image(systemName: "play") | |
case .success: | |
Image(systemName: "checkmark") | |
case .failure: | |
Image(systemName: "xmark") | |
} | |
} | |
var color: Color { | |
switch self { | |
case .waiting: | |
return .gray | |
case .running: | |
return .blue | |
case .success: | |
return .green | |
case .failure: | |
return .red | |
} | |
} | |
} | |
struct AppCellLabelStyle: LabelStyle { | |
var reversed: Bool | |
func makeBody(configuration: Configuration) -> some View { | |
Label { | |
makeTitle(configuration.title) | |
.frame(maxWidth: .greatestFiniteMagnitude, alignment: reversed ? .trailing : .leading) | |
} icon: { | |
makeIcon(configuration.icon) | |
} | |
.environment(\.layoutDirection, reversed ? .rightToLeft : .leftToRight) | |
} | |
func makeTitle<T: View>(_ title: T) -> some View { | |
title | |
.foregroundColor(.primary) | |
.font(.headline) | |
} | |
func makeIcon<T: View>(_ icon: T) -> some View { | |
icon | |
.padding(5) | |
.background(.quaternary, in: RoundedRectangle(cornerRadius: 4)) | |
} | |
} | |
extension LabelStyle where Self == AppCellLabelStyle { | |
static func applicationCell(reversed: Bool = false) -> AppCellLabelStyle { | |
.init(reversed: reversed) | |
} | |
} | |
struct ContentView: View { | |
let applications = [ | |
Application( | |
name: "My App", | |
icon: .systemImage("gear"), | |
color: .orange, | |
builds: [ | |
.init(status: .waiting), | |
.init(status: .running), | |
.init(status: .success), | |
.init(status: .failure), | |
.init(status: .success), | |
.init(status: .success), | |
] | |
) | |
] | |
var body: some View { | |
NavigationView { | |
List(applications) { application in | |
NavigationLink { | |
List(application.builds) { build in | |
NavigationLink { | |
List { | |
Section { | |
build.status | |
.font(.title3.bold()) | |
.symbolVariant(.fill) | |
.listRowBackground( | |
Rectangle() | |
.fill(.quaternary) | |
.foregroundStyle(build.status.color) | |
) | |
} | |
Section { | |
application | |
.labelStyle(.applicationCell(reversed: true)) | |
build | |
.font(.headline) | |
.badge("Build #") | |
} footer: { | |
VStack { | |
Button { | |
/// Do things... | |
} label: { | |
Label("Rebuild", systemImage: "arrow.counterclockwise.circle") | |
.frame(maxWidth: .greatestFiniteMagnitude) | |
} | |
.keyboardShortcut(.defaultAction) | |
.buttonStyle(.borderedProminent) | |
.font(.headline) | |
.disabled(build.status == .waiting) | |
Button(role: .destructive) { | |
/// Do things... | |
} label: { | |
Label("Cancel", systemImage: "xmark.circle") | |
.frame(maxWidth: .greatestFiniteMagnitude) | |
} | |
.buttonStyle(.bordered) | |
.tint(.red) | |
.font(.headline.weight(.regular)) | |
} | |
.controlSize(.large) | |
.padding(.vertical) | |
.listRowInsets(.init()) | |
} | |
} | |
.navigationBarTitle("Build Details") | |
.tint(build.status.color) | |
} label: { | |
Label { | |
VStack(alignment: .leading) { | |
build.font(.headline) | |
Text("\(Text("started:")) \(build.createdAt, style: .relative)") | |
.font(.subheadline) | |
.foregroundStyle(.secondary) | |
} | |
} icon: { | |
build.status | |
.labelStyle(.iconOnly) | |
.font(.headline) | |
.symbolVariant(.fill.circle) | |
.symbolRenderingMode(.hierarchical) | |
} | |
} | |
} | |
.navigationBarTitle("Builds") | |
} label: { | |
application | |
.labelStyle(.applicationCell()) | |
} | |
} | |
.navigationBarTitle("Applications") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment