前言
此次小组件实战需求参考 iOS 小组件开发第三篇:实战 ,记录开发和实现时间状态提醒小组件的过程,代码有所改动,末尾附完整代码示例。
需求
需求描述:实现一个可以展示上午、下午、夜里三种状态的小组件,分别用三个不同的图标来显示。
按照上述需求,我们简单地把这个小组件的时间线描述为 3个 阶段:
-
早上 0 点到中午 12 点为 上午
-
中午 12 点到下午 18 点为 下午
-
下午 18 点到早上 24 点为 夜里
实现
1.TimelineEntry
TimelineEntry 是用来提供小组件显示内容和时间的,需要先写一个展示时间状态的枚举,用来表示上午、下午和晚上
enum TodayTime {
case morning, afternoon, night
}
struct TodayEntry: TimelineEntry {
let date: Date
// 表示上午、下午、晚上
let time: TodayTime
}
2.Provider时间线
先写一个获取当天时间的方法
// 指定当天时间
func getDate(hour: Int) -> Date {
let calendar = Calendar.current
var components = calendar.dateComponents([.year, .month, .day], from: Date())
components.hour = hour
components.minute = 0
components.second = 0
return calendar.date(from: components)!
}
时间线数据整理
struct TodayProvider: TimelineProvider {
func placeholder(in context: Context) -> TodayEntry {
TodayEntry(date: Date(), time: .morning)
}
func getSnapshot(in context: Context, completion: @escaping (TodayEntry) -> ()) {
let entry = TodayEntry(date: Date(), time: .morning)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [TodayEntry] = []
// 根据当前时间指定刷新时间线
let hour = Calendar.current.component(.hour, from: Date())
switch hour {
case 0..<12:
entries.append(TodayEntry(date: Date(), time: .morning))
entries.append(TodayEntry(date: getDate(hour: 12), time: .afternoon))
entries.append(TodayEntry(date: getDate(hour: 18), time: .night))
case 12..<18:
entries.append(TodayEntry(date: Date(), time: .afternoon))
entries.append(TodayEntry(date: getDate(hour: 18), time: .night))
default:
entries.append(TodayEntry(date: Date(), time: .night))
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
3.UI展示
根据不同的 case 返回不同的图标和标题:
有两种方式,第一种直接在 TodayTime 枚举添加方法
enum TodayTime {
case morning, afternoon, night
var text: String {
switch self {
case .morning:
return "上午"
case .afternoon:
return "下午"
case .night:
return "晚上"
}
}
var icon: String {
switch self {
case .morning:
return "sunrise"
case .afternoon:
return "sun.max.fill"
case .night:
return "sunset"
}
}
}
第二种利用 extension 扩展
extension TodayTime {
var text: String {
switch self {
case .morning:
return "上午"
case .afternoon:
return "下午"
case .night:
return "晚上"
}
}
var icon: String {
switch self {
case .morning:
return "sunrise"
case .afternoon:
return "sunset"
case .night:
return "moon.stars"
}
}
}
重新排版入口组件
struct TodayWidgetEntryView : View {
var entry: TodayProvider.Entry
var body: some View {
VStack(spacing: 10) {
Image(systemName: entry.time.icon)
.imageScale(.large)
.foregroundColor(.red)
HStack {
Text("\(entry.time.text)好")
}
.font(.title3)
}
.widgetBackground(Color.white)
}
}
效果
完整代码
import SwiftUI
import WidgetKit
enum TodayTime {
case morning, afternoon, night
var text: String {
switch self {
case .morning:
return "上午"
case .afternoon:
return "下午"
case .night:
return "晚上"
}
}
var icon: String {
switch self {
case .morning:
return "sunrise"
case .afternoon:
return "sunset"
case .night:
return "moon.stars"
}
}
}
struct TodayEntry: TimelineEntry {
let date: Date
// 表示上午、下午、晚上
let time: TodayTime
}
// 指定当天时间
func getDate(hour: Int) -> Date {
let calendar = Calendar.current
var components = calendar.dateComponents([.year, .month, .day], from: Date())
components.hour = hour
components.minute = 0
components.second = 0
return calendar.date(from: components)!
}
extension View {
// 背景
@ViewBuilder
func widgetBackground(_ backgroundView: some View) -> some View {
// 如果是 iOS 17,则使用 containerBackground
if #available(iOS 17.0, *) {
containerBackground(for: .widget) {
backgroundView
}
} else {
background(backgroundView)
}
}
}
struct TodayProvider: TimelineProvider {
func placeholder(in context: Context) -> TodayEntry {
TodayEntry(date: Date(), time: .morning)
}
func getSnapshot(in context: Context, completion: @escaping (TodayEntry) -> ()) {
let entry = TodayEntry(date: Date(), time: .morning)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [TodayEntry] = []
// 根据当前时间指定刷新时间线
let hour = Calendar.current.component(.hour, from: Date())
switch hour {
case 0..<12:
entries.append(TodayEntry(date: Date(), time: .morning))
entries.append(TodayEntry(date: getDate(hour: 12), time: .afternoon))
entries.append(TodayEntry(date: getDate(hour: 18), time: .night))
case 12..<18:
entries.append(TodayEntry(date: Date(), time: .afternoon))
entries.append(TodayEntry(date: getDate(hour: 18), time: .night))
default:
entries.append(TodayEntry(date: Date(), time: .night))
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct TodayWidgetEntryView : View {
var entry: TodayProvider.Entry
var body: some View {
VStack(spacing: 10) {
Image(systemName: entry.time.icon)
.imageScale(.large)
.foregroundColor(.red)
HStack {
Text("\(entry.time.text)好")
}
.font(.title3)
}
.widgetBackground(Color.white)
}
}
struct TodayWidget: Widget {
let kind: String = "TextWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: TodayProvider()) { entry in
TodayWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
.supportedFamilies([.systemMedium])
}
}
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。