iOS widget 小组件 秒级刷新

Text 控件

系统 Text控件 自带有时间计算的功能

优点: 不需要额外设置,系统级别刷新时间

缺点: 不能自定义

style 例子 说明
time 12:00 AM 时分 时分 上午/下午
date date June 3, 2019 年月日
relative 2 houers, 23 min/1 year, 1 month 写入日期对比现在日期的差多少 格式是 时分/年月
offset +2 hours/-3 months 写入日期对比现在日期的差多少 格式是 +多少/-多少
timer 10:30:11 时分秒

效果

16774703315351.png

实现

import WidgetKit
import SwiftUI
import Intents

struct TimerClockProvider: IntentTimelineProvider {
    func placeholder(in context: Context) -> TimerClockEntry {
        TimerClockEntry(date: Date(), configuration: TimerClockIntent(), displaySize: context.displaySize)
    }

    func getSnapshot(for configuration: TimerClockIntent, in context: Context, completion: @escaping (TimerClockEntry) -> ()) {
        let entry = TimerClockEntry(date: Date(), configuration: configuration, displaySize: context.displaySize)
        completion(entry)
    }

    func getTimeline(for configuration: TimerClockIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
        var entries: [TimerClockEntry] = []

        
        // 系统 text .timer
        let currentDate = Date()

        let entryDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!
        let entry = TimerClockEntry(date: entryDate, configuration: configuration, displaySize: context.displaySize)
        entries.append(entry)

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    
  
    }
}

struct TimerClockEntry: TimelineEntry {
    let date: Date
    let configuration: TimerClockIntent
    let displaySize: CGSize
}

// MARK: - Entry
struct TimerClockEntryView : View {
    var entry: TimerClockProvider.Entry

    var body: some View {
        return VStack(alignment: .center) {
            
            // 系统 text .timer
            TimerClockView()
            
            
        }
        .background(
            Rectangle().fill(Color.black)
                .frame(width: entry.displaySize.width, height: entry.displaySize.height, alignment: .center)
        )
    }
}

// MARK: WidgetConfiguration
struct TimerClock: Widget {
    let kind: String = "TimerClock"
    var title: String = ""
    var desc: String = ""

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: TimerClockIntent.self, provider: TimerClockProvider()) { entry in
            TimerClockEntryView(entry: entry)
        }
        .configurationDisplayName(title)
        .description(desc)
        .supportedFamilies([.systemSmall])
    }
}



struct TimerClockView: View {
    
    var body: some View {
        VStack {
            
            Spacer()
            Text("text .timer")
                .foregroundColor(.red)

            Spacer()
  
            Text(Date().getCurrentDayStart(true), style: .timer)
                .multilineTextAlignment(.center)
                .foregroundColor(.red)
            Spacer()
            
        }
    }
}


//获取当天开始的日期,给Date增加一个拓展方法
extension Date {
   func getCurrentDayStart(_ isDayOf24Hours: Bool)-> Date {
       let calendar:Calendar = Calendar.current;
       let year = calendar.component(.year, from: self);
       let month = calendar.component(.month, from: self);
       let day = calendar.component(.day, from: self);

       let components = DateComponents(year: year, month: month, day: day, hour: 0, minute: 0, second: 0)
       return Calendar.current.date(from: components)!
   }
}



添加TimeLine

添加TimeLine 让小组件定时刷新

优点: 可以自定义UI

缺点: 因为小组件刷新机制限制,小组件会卡顿。

小组件一天可以刷新40至70次。在iOS 16以下系统。可以设置一天的TimeLine。即是((24 * 60 * 60)/70)。所以iOS 16之后只能设置((24 * 60 * 60)/40)。所以会导致小组件有时候卡顿,不刷新。

TimelineReloadPolicy

let timeline = Timeline(entries: entries, policy: .never)
TimelineReloadPolicy 说明
atEnd 等最后一个timeline 执行完刷新
never 不自动重新加载
after 指定时间刷新

倒计时小组件

在 getTimeline 设置刷新机制。 一分钟刷新

        for hourOffset in 0 ..< 61 {
            let entryDate = Calendar.current.date(byAdding: .second, value: hourOffset, to: currentDate)!
            let num = 60 - hourOffset
            let entry = CountdownClockEntry(date: entryDate, configuration: configuration, displaySize: context.displaySize, num: num)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .never)

效果

截屏2023-02-27 14.20.58.png

实现

import WidgetKit
import SwiftUI
import Intents

struct CountdownClockProvider: IntentTimelineProvider {
    func placeholder(in context: Context) -> CountdownClockEntry {
        CountdownClockEntry(date: Date(), configuration: CountdownClockIntent(), displaySize: context.displaySize, num: 0)
    }

    func getSnapshot(for configuration: CountdownClockIntent, in context: Context, completion: @escaping (CountdownClockEntry) -> ()) {
        let entry = CountdownClockEntry(date: Date(), configuration: configuration, displaySize: context.displaySize, num: 0)
        completion(entry)
    }

    func getTimeline(for configuration: CountdownClockIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
        var entries: [CountdownClockEntry] = []

        // 倒计时
        let currentDate = Date()

        for hourOffset in 0 ..< 61 {
            let entryDate = Calendar.current.date(byAdding: .second, value: hourOffset, to: currentDate)!
            let num = 60 - hourOffset
            let entry = CountdownClockEntry(date: entryDate, configuration: configuration, displaySize: context.displaySize, num: num)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .never)
        completion(timeline)
    }
}

struct CountdownClockEntry: TimelineEntry {
    let date: Date
    let configuration: CountdownClockIntent
    let displaySize: CGSize
    let num: Int
}

// MARK: - Entry
struct CountdownClockEntryView : View {

    var entry: CountdownClockProvider.Entry
    
    var body: some View {

        return VStack(alignment: .center) {
            // 倒计时
            CountdownDemo(countdown: entry.num)
        }
        .background(
            Rectangle().fill(Color.black)
                .frame(width: entry.displaySize.width, height: entry.displaySize.height, alignment: .center)
        )

    }

}

// MARK: WidgetConfiguration
struct CountdownClock: Widget {
    let kind: String = "CountdownClock"
    var title: String = ""
    var desc: String = ""

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: CountdownClockIntent.self, provider: CountdownClockProvider()) { entry in
            CountdownClockEntryView(entry: entry)
        }
        .configurationDisplayName(title)
        .description(desc)
        .supportedFamilies([.systemSmall])
    }
}


// MARK: - 倒计时
struct CountdownDemo: View {

    var countdown: Int
    
    
    var body: some View {
        VStack {
            
            Spacer()
            Text("倒计时")
                .foregroundColor(.red)

            Spacer()
            
            Text(countdown.description)
                .multilineTextAlignment(.center)
                .foregroundColor(.red)
            Spacer()
        }
    }
    
}

时钟小组件

效果


截屏2023-02-27 15.05.06.png

实现

import WidgetKit
import SwiftUI
import Intents

struct CustomClockProvider: IntentTimelineProvider {
    func placeholder(in context: Context) -> CustomClockEntry {
        CustomClockEntry(date: Date(), configuration: CustomClockIntent(), displaySize: context.displaySize)
    }

    func getSnapshot(for configuration: CustomClockIntent, in context: Context, completion: @escaping (CustomClockEntry) -> ()) {
        let entry = CustomClockEntry(date: Date(), configuration: configuration, displaySize: context.displaySize)
        completion(entry)
    }

    func getTimeline(for configuration: CustomClockIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
        var entries: [CustomClockEntry] = []

        // 自定义
        let currentDate = Date()

        // 小组件 一天有 40 ~ 70 次刷新机会
        var time = ((24 * 60 * 60)/40)
        if #available(iOS 16, *) {
            time = ((24 * 60 * 60)/70)
        }
        
        for hourOffset in 0 ..< time {
            let entryDate = Calendar.current.date(byAdding: .second, value: hourOffset, to: currentDate)!
            let entry = CustomClockEntry(date: entryDate, configuration: configuration, displaySize: context.displaySize)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
  
    }
}

struct CustomClockEntry: TimelineEntry {
    let date: Date
    let configuration: CustomClockIntent
    let displaySize: CGSize
}

// MARK: - Entry
struct CustomClockEntryView : View {
    
    @State var index = 0
    @State var start: Date = Date()
    
    var entry: CustomClockProvider.Entry
    
    var body: some View {
        let calendar = Calendar.current
        let dateComponents = calendar.dateComponents([.hour, .minute, .second], from: entry.date)
        var hour:Int = 0
        var min:Int = 0
        var second:Int = 0
        
        if let h =  dateComponents.hour,
           let m = dateComponents.minute,
           let s = dateComponents.second {
            
            hour = h
            min = m
            second = s
        }
        
        
        return VStack(alignment: .center) {

            // 自定义
            CustomClockDemo(hour: hour, min: min, second: second)
            
        }
        .background(
            Rectangle().fill(Color.black)
                .frame(width: 300, height: 300, alignment: .center)
        )

    }

}

// MARK: WidgetConfiguration
struct CustomClock: Widget {
    let kind: String = "CustomClock"
    var title: String = ""
    var desc: String = ""
    
    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: CustomClockIntent.self, provider: CustomClockProvider()) { entry in
            CustomClockEntryView(entry: entry)
        }
        .configurationDisplayName(title)
        .description(desc)
        .supportedFamilies([.systemSmall])
    }
}


// MARK: - CustomClock
struct CustomClockDemo: View {
    var hour: Int = 0
    var min: Int = 0
    var second: Int = 0
    
    var body: some View {
        VStack {
            
            Spacer()
            Text("自定义")
                .foregroundColor(.red)

            Spacer()
            
            Text("\(hour):\(min):\(second)")
                .foregroundColor(.red)
            Spacer()
        }
    }
}

Github地址

懒得打的可以直接打开项目查看 Github地址 https://github.com/HahnLoving/iOS_Study

你可能感兴趣的:(iOS widget 小组件 秒级刷新)