Skip to content

Instantly share code, notes, and snippets.

@ABashkirova
Created September 26, 2021 13:58
Show Gist options
  • Save ABashkirova/b0ab13b121145b7d70c7aab33bb3fbd4 to your computer and use it in GitHub Desktop.
Save ABashkirova/b0ab13b121145b7d70c7aab33bb3fbd4 to your computer and use it in GitHub Desktop.
import SwiftUI
import PlaygroundSupport
struct WidgetWithCustomTimeView: View {
@State var hour: DateComponents = Date.currentHour
@State var minute: DateComponents = Date.currentMinutes
@State var timeColonState: ColonState = .paused
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var backgroundColor = Color(red: 0.961, green: 0.957, blue: 0.949)
var body: some View {
VStack(alignment: .center, spacing: 8) {
widget
Button(timeColonState == .paused ? "Start" : "Stop", action: {
switch timeColonState {
case .paused:
timeColonState = timeColonState.start()
case .started:
timeColonState = timeColonState.pause()
}
})
}
.padding(15)
.background(.black)
.onReceive(timer) { _ in
self.updateTime()
self.updateColonColorIfNeeded()
}
}
// MARK: Display logic
private func updateColonColorIfNeeded() {
if timeColonState != .paused {
withAnimation(Animation.easeOut(duration: 1).delay(1)
) {
timeColonState = timeColonState.toggle()
}
}
}
private func updateTime() {
hour = Date.currentHour
minute = Date.currentMinutes
}
private func dotColor(_ dot: ColonState.FilledDot) -> Color {
return timeColonState == .started(dot) ? .red : .black
}
private func formatter(_ allowedUnit: NSCalendar.Unit) -> DateComponentsFormatter {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [allowedUnit]
return formatter
}
// MARK: - Subviews
// MARK: Widget
private var widget: some View {
ZStack {
widgetBody
widgetContent
.padding(15)
}
.frame(width: 150, height: 150, alignment: .center)
}
private var widgetBody: some View {
RoundedRectangle(cornerRadius: 10)
.foregroundColor(backgroundColor)
}
private var widgetContent: some View {
VStack(alignment: .leading) {
time
searchBar
}
}
// MARK: Time
private var time: some View {
HStack(alignment: .bottom, spacing: 0) {
Text(formatter(.hour).string(from: hour) ?? "")
.labelStyle(WidgetLabelStyle(size: 30))
colonView
Text(formatter(.minute).string(from: minute) ?? "")
.labelStyle(WidgetLabelStyle(size: 30))
}
}
private var dotView: some View {
Text(".").labelStyle(WidgetLabelStyle(size: 30))
}
private var colonView: some View {
VStack(alignment: .trailing, spacing: -14) {
dotView.foregroundColor(dotColor(.top))
dotView.foregroundColor(dotColor(.bottom))
}
}
// MARK: Search
private var searchBar: some View {
ZStack(alignment: .leading) {
searchBarContainer
searchIcon.padding(.leading, 15)
}
}
private var searchBarContainer: some View {
RoundedRectangle(cornerRadius: 20)
.frame(height: 40)
.foregroundColor(.white)
}
private var searchIcon: some View {
Text("Y")
.labelStyle(WidgetLabelStyle(size: 24))
.foregroundColor(.red)
}
}
struct WidgetLabelStyle: LabelStyle {
private let size: CGFloat
init(size: CGFloat) {
self.size = size
}
func makeBody(configuration: Configuration) -> some View {
Label(configuration)
.font(.system(size: size, weight: .light))
}
}
// MARK: Color state
enum ColonState: Equatable {
case paused
case started(FilledDot)
enum FilledDot: Equatable {
case top
case bottom
func toggle() -> FilledDot {
switch self {
case .bottom: return .top
case .top: return .bottom
}
}
}
func pause() -> ColonState {
return .paused
}
func start() -> ColonState {
return .started(.top)
}
func toggle() -> ColonState {
switch self {
case .paused:
return self.start()
case .started(let dot):
return .started(dot.toggle())
}
}
}
// MARK: Date + utils
extension Date {
static var currentHour: DateComponents {
Date().component(.hour)
}
static var currentMinutes: DateComponents {
Date().component(.minute)
}
func component(_ component: Calendar.Component) -> DateComponents {
return Calendar.current.dateComponents([component], from: self)
}
}
PlaygroundPage.current.setLiveView(WidgetWithCustomTimeView())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment