// // LaunchWidgetLiveActivity.swift // LaunchWidget // // Created by Laurent Morvillier on 25/01/2023. // import WidgetKit import SwiftUI import ActivityKit struct LiveActivityView: View { var name: String var endDate: Date var body: some View { HStack { Text(self.name) Spacer() Text(self.endDate, style: .timer) .monospaced() }.padding() .font(.title) } } struct LaunchWidgetLiveActivity: Widget { fileprivate let now: Date = Date() var body: some WidgetConfiguration { ActivityConfiguration(for: LaunchWidgetAttributes.self) { context in // TimelineView(.periodic(from: self.now, by: 0.1)) { _ in // Lock screen/banner UI goes here VStack(alignment: .leading) { let date: Date = self._date(context: context) let name: String = self._name(context: context) if context.attributes.isCountdown { let range = Date()...date Text(timerInterval: range, pauseTime: range.lowerBound) .font(.title) } else { Text(context.attributes.date, style: .timer) .font(.title) } Text(name.uppercased()) .font(.callout) } // } .padding() .monospaced() .background(Color(white: 0.1)) .foregroundColor(.white) .activitySystemActionForegroundColor(.white) } dynamicIsland: { context in DynamicIsland { // Expanded UI goes here. Compose the expanded UI through // various regions, like leading/trailing/center/bottom DynamicIslandExpandedRegion(.leading) { Text(context.attributes.name.uppercased()) .monospaced() .padding(.leading, 4.0) } DynamicIslandExpandedRegion(.trailing) { Text(context.attributes.date, style: .timer) .monospaced() .padding(.trailing, 4.0) } DynamicIslandExpandedRegion(.bottom) { Button { self._stop() } label: { Text("Stop") } } } compactLeading: { Text(context.attributes.name.uppercased()) } compactTrailing: { Group { if context.attributes.isCountdown { let range = Date()...context.attributes.date Text(timerInterval: range, pauseTime: range.lowerBound) } else { Text(context.attributes.date, style: .timer) } }.multilineTextAlignment(.trailing) } minimal: { if context.attributes.isCountdown { let range = Date()...context.attributes.date Text(timerInterval: range, pauseTime: range.lowerBound) .font(Font.system(size: 11.0)) } else { Text(context.attributes.date, style: .timer) .font(Font.system(size: 11.0)) } } // .widgetURL(URL(string: context.attributes.id)) .keylineTint(Color.red) } } fileprivate func _name(context: ActivityViewContext) -> String { if let sequence = context.state.sequence, sequence.steps.count > 1 { return context.attributes.name } else if let name = context.state.sequence?.currentStep.name { return name } else { return context.attributes.name } } fileprivate func _date(context: ActivityViewContext) -> Date { if let sequence = context.state.sequence, sequence.steps.count > 1 { return sequence.end } else if let date = context.state.sequence?.currentStep.interval.end { return date } else { return context.attributes.date } } fileprivate func _stop() { } } struct LaunchWidgetLiveActivity_Previews: PreviewProvider { static let attributes = LaunchWidgetAttributes( id: "", name: "Tea", date: Date().addingTimeInterval(3600.0), isCountdown: true) static let contentState = LaunchWidgetAttributes.ContentState(ended: false) static var previews: some View { if #available(iOS 16.2, *) { attributes .previewContext(contentState, viewKind: .dynamicIsland(.compact)) .previewDisplayName("Island Compact") attributes .previewContext(contentState, viewKind: .dynamicIsland(.expanded)) .previewDisplayName("Island Expanded") attributes .previewContext(contentState, viewKind: .dynamicIsland(.minimal)) .previewDisplayName("Minimal") attributes .previewContext(contentState, viewKind: .content) .previewDisplayName("Notification") } else { Text("no preview available") } } }