Skip to content

Instantly share code, notes, and snippets.

@mortenjust
Created January 23, 2023 12:17
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mortenjust/a8bcbd481b8379ec46ca8c640c33db9b to your computer and use it in GitHub Desktop.
Save mortenjust/a8bcbd481b8379ec46ca8c640c33db9b to your computer and use it in GitHub Desktop.
//
// DarkModeMasker.swift
// SwiftUI Demos
//
// Created by Morten Just on 1/22/23.
// https://twitter.com/joshguospace/status/1617101052192649216?s=12
import SwiftUI
import Charts
struct DarkModeMasker: View {
@State var name = ""
@State var sales = [Int]()
@State var dates = [Int]()
@State var isLight = false
func DataPoint(_ data: String, label: String) -> some View {
VStack {
Text(data).font(.largeTitle).opacity(0.9)
Text(label).font(.caption).opacity(0.5)
}
}
func ModeToggle() -> some View {
HStack {
Spacer()
Toggle(isOn: $isLight) {
Text("Light")
}
.toggleStyle(.switch)
.padding()
}
}
func MainView(_ isDark : Bool) -> some View {
VStack {
ModeToggle()
VStack {
let chartStroke = isDark ? Color.yellow : Color.blue
let chartFill = LinearGradient(
colors: [chartStroke.opacity(0.5), .clear],
startPoint: .top,
endPoint: .bottom)
let dateWindow = dates.suffix(30)
let salesWindow = sales.suffix(30)
Text("Sales").font(.largeTitle.bold())
.frame(maxWidth: .infinity, alignment: .leading)
Chart {
ForEach(dateWindow, id: \.self) {
// fill
AreaMark(x: .value("Date", $0), y: .value("Sales", sales[$0]))
.foregroundStyle(chartFill)
// .interpolationMethod(.catmullRom)
// line/border
LineMark(x: .value("Date", $0), y: .value("Sales", sales[$0]))
.foregroundStyle(chartStroke)
// .interpolationMethod(.catmullRom)
}
if !dates.isEmpty {
// average
RuleMark(y: .value("Average", (sales.reduce(0, +) / sales.count) ))
.lineStyle(.init(lineWidth: 0.5))
.foregroundStyle(
isDark ? Color.orange : Color.purple
)
// dot
PointMark(x: .value("Today", dates[8]), y: .value("Sales", sales[8]))
.foregroundStyle(
isDark
? Color.white.shadow(.drop(color: .white, radius: 2))
: Color.black.shadow(.drop(radius: 0))
)
}
}
.chartXScale(domain: (dateWindow.first ?? 0)...(dateWindow.last ?? 0))
HStack(spacing: 35) {
DataPoint("$393", label: "Avg.")
DataPoint("$29", label: "Per min")
DataPoint("$529", label: "Max")
}.padding(.top)
}
.padding(30)
.background { Color.primary.opacity(0.04)}
.cornerRadius(10)
.padding(30)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background {
Rectangle().fill(Color(nsColor: isDark ? .black : .windowBackgroundColor))
}
.colorScheme(isDark ? .dark : .light)
}
@State var sliderX : CGFloat = 0
var body: some View {
ZStack {
GeometryReader { reader in
let w = reader.size.width
MainView(true)
MainView(false)
.mask {
ZStack {
HStack { // draggable mask
Rectangle()
.frame(width: sliderX)
Spacer()
}
Circle() // circle mask
.position(x: w, y:0)
.scaleEffect(isLight ? 4 : 0, anchor: .topTrailing)
}
}
VStack {
Spacer()
// slider
Capsule()
.position(x: sliderX)
.frame(width: 15, height: 10)
.simultaneousGesture(DragGesture()
.onChanged({ value in
sliderX = min(max(value.location.x, 0), w)
})
)
Spacer()
}
}
}
.animation(.default, value: isLight)
.ignoresSafeArea()
.onTapGesture { // click anywhere to toggle
isLight.toggle()
}
// initial dummy data
.onAppear {
sales = (0...32).map { Int.random(in: 10...50) + $0 * 2 }
dates = (0...30).map { $0 }
} // streaming dummy data
.onReceive(Timer.publish(every: 0.67, on: .main, in: .default).autoconnect(), perform: { _ in
sales.append(Int.random(in: 10...50) + sales.count)
dates.append(dates.last! + 1)
})
}
}
struct DarkModeMasker_Previews: PreviewProvider {
static var previews: some View {
DarkModeMasker()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment