Skip to content

Instantly share code, notes, and snippets.

@rr-codes
Created July 22, 2020 04:12
Show Gist options
  • Select an option

  • Save rr-codes/8e18298dd7d9cb95aeae8d50a1b9503d to your computer and use it in GitHub Desktop.

Select an option

Save rr-codes/8e18298dd7d9cb95aeae8d50a1b9503d to your computer and use it in GitHub Desktop.
//
// CardView2.swift
// Hourglass
//
// Created by Richard Robinson on 2020-07-21.
//
import Foundation
import SwiftUI
// MARK: Example
let example: some View = CardView2Factory.create(
colors: (.red, .blue),
name: "My Birthday",
range: .init(start: Date(), duration: 0),
configuration: (size: .small, style: .inverted)
)
// MARK: Factory
class CardView2Factory {
enum Size {
case small, medium
}
enum Style {
case plain, inverted
}
@ViewBuilder static func create(
colors: (Color, Color),
name: String,
range: DateInterval,
configuration: (size: Size, style: Style)
) -> some View {
switch configuration {
case (.small, .plain):
SmallPlainCardView2(colors: colors, name: name, range: range)
case (.small, .inverted):
SmallInvertedCardView2(colors: colors, name: name, range: range)
case (.medium, .plain):
MediumPlainCardView2(colors: colors, name: name, range: range)
case (.medium, .inverted):
MediumInvertedCardView2(colors: colors, name: name, range: range)
}
}
}
// MARK: Main Protocol
protocol CardView2: View {
associatedtype Accent: ShapeStyle & View
associatedtype Background: ShapeStyle
var colors: (Color, Color) { get }
var name: String { get }
var range: DateInterval { get }
var headerTextColor: Color { get }
var accent: Accent { get }
var background: Background { get }
}
// MARK: Size Protocols
extension CardView2 {
fileprivate var dateFormatter: DateFormatter {
let df = DateFormatter()
df.doesRelativeDateFormatting = true
df.dateStyle = .medium
df.timeStyle = .none
return df
}
fileprivate var dateComponentsFormatter: DateComponentsFormatter {
let dcf = DateComponentsFormatter()
dcf.allowedUnits = [.day, .hour, .minute, .second]
dcf.unitsStyle = .short
dcf.maximumUnitCount = 2
return dcf
}
fileprivate var mainText: Text {
#if WIDGET
let text = Text(range.end, style: .relative)
#else
let text = Text(
dateComponentsFormatter.string(from: range.end.timeIntervalSinceNow)!
)
#endif
return range.progress < 1.0 ? text : Text("Complete!")
}
fileprivate var shape: some Shape {
#if WIDGET
return Rectangle()
#else
return RoundedRectangle(cornerRadius: 23.0, style: .continuous)
#endif
}
fileprivate var text: some View {
VStack {
Text(name)
.font(.headline)
.fontWeight(.bold)
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(headerTextColor)
.padding(.bottom, 2)
Text(dateFormatter.string(from: range.end))
.font(.subheadline)
.fontWeight(.medium)
.foregroundColor(headerTextColor.opacity(0.75))
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
protocol SmallCardView2: CardView2 {
}
extension SmallCardView2 {
var body: some View {
shape.fill(background)
.overlay(
VStack {
self.text
Spacer()
mainText.font(
Font.system(.title3, design: .rounded)
.monospacedDigit()
.weight(.bold)
)
.tracking(-0.1)
.foreground(accent)
.padding(.bottom, 11.0)
Spacer()
ProgressView2(value: range.progress, fill: accent)
}
.padding(.bottom, 24)
.padding(.top, 22)
.padding(.horizontal)
)
}
}
protocol MediumCardView2: CardView2 {
}
extension MediumCardView2 {
var body: some View {
shape.fill(background)
.overlay(
HStack(alignment: VerticalAlignment.center) {
VStack {
self.text
Spacer()
mainText.font(
Font.system(.title, design: .rounded)
.monospacedDigit()
.weight(.bold)
)
.tracking(-0.1)
.foreground(accent)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.bottom, 5.0)
}
RingView(progress: 0.5, diameter: 85.0, colors: colors, textColor: headerTextColor)
.padding(.trailing, 10)
.padding(.bottom, 8)
}
.padding(.top, 34)
.padding(.bottom, 32)
.padding(.horizontal, 27)
)
}
}
// MARK: Style Protocols
protocol PlainCardView2: CardView2 {
}
extension PlainCardView2 {
var headerTextColor: Color {
return .black
}
var accent: LinearGradient {
return LinearGradient(
gradient: .init(colors: [colors.0, colors.1]),
startPoint: .topTrailing,
endPoint: .bottomLeading
)
}
var background: Color {
return .white
}
}
protocol InvertedCardView2: CardView2 {
}
extension InvertedCardView2 {
var headerTextColor: Color {
return .white
}
var accent: Color {
return .white
}
var background: LinearGradient {
LinearGradient(
gradient: .init(colors: [colors.0, colors.1]),
startPoint: .topTrailing,
endPoint: .bottomLeading
)
}
}
// MARK: Implementation
struct SmallPlainCardView2: SmallCardView2, PlainCardView2 {
let colors: (Color, Color)
let name: String
let range: DateInterval
}
struct SmallInvertedCardView2: SmallCardView2, InvertedCardView2 {
let colors: (Color, Color)
let name: String
let range: DateInterval
}
struct MediumPlainCardView2: MediumCardView2, PlainCardView2 {
let colors: (Color, Color)
let name: String
let range: DateInterval
}
struct MediumInvertedCardView2: MediumCardView2, InvertedCardView2 {
let colors: (Color, Color)
let name: String
let range: DateInterval
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment