Widget动画实践

1. Widget动画基础知识

1. iOS17之前是不支持动画的,iOS17之后支持了转场动画

2. 有私有方法,可以实现小组件动画。但是app上架很危险

     参考(利用私有api):https://github.com/TopWidgets/SwingAnimation

2. Widget动画实践,iOS17+

2.1 利用转场动画,实现简单过渡

注意点

1. 代码创建了2000个entry,实际上在不同机型上表现不一,有可正常运行也有运行不起来的。具体原因没探究出来,猜测与内存占用有关。所以这里创建多少entry,需要根据项目实际情况调研

2. 动画仅在iOS17+有用 

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), message: "")
    }
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date(), message: "")
        completion(entry)
    }
    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        //1. 定义2000个entry,在一条timeline中。
        //2. 每个entry间隔3秒
        let date = Date()
        var entries: [Entry] = []
        for index in 0...2000 {
            let entry = SimpleEntry(date: date + Double(index) * 3.0, message: "当前entry:\(index)")
            entries.append(entry)
        }
        
        //.atEnd, 所有entry结束后刷新时间线
        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let message: String
}

struct widgetEntryView : View {
    var entry: Provider.Entry
    var body: some View {
        Text(entry.message)
            .font(.title)
            .foregroundColor(Color.red)
        
            //转场动画配置
            .id(entry.message)
            .transition(.asymmetric(insertion: .move(edge: .bottom).combined(with: .opacity), removal: .move(edge: .top).combined(with: .opacity)))
            .animation(.easeInOut(duration: 0.3), value: entry.message)
    }
}

struct widget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "kind", provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                widgetEntryView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
            } else {
                widgetEntryView(entry: entry)
                    .padding()
                    .background(Color.clear)
            }
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

2.2 利用Toggle | Button,  加上.invalidatableContent(),  实现闪动的效果

注意点

.invalidatableContent(),看介绍不像是会有这种效果的。但是确实出现了这个效果,具体没深入探究

import WidgetKit
import SwiftUI
import AppIntents

/// 点击交互用AppIntent
@available(iOS 17.0, *)
struct TestIntent: AppIntent {
    static var title: LocalizedStringResource = "button press"
    static var description: IntentDescription? = IntentDescription(stringLiteral: "none")
    
    func perform() async throws -> some IntentResult {
        
        //等待5秒,模仿网络请求
        await withCheckedContinuation { c in
            DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
                c.resume()
            }
        }
        
        return .result()
    }
}

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), message: "")
    }
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date(), message: "")
        completion(entry)
    }
    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        let timeline = Timeline(entries: [SimpleEntry(date: Date(), message: "")], policy: .never)
        completion(timeline)
        
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let message: String
}

struct widgetEntryView : View {
    var entry: Provider.Entry
    var body: some View {
        if #available(iOS 17, *) {
            VStack(spacing: 5) {
                Text("数据刷新中")
                    .font(.title).foregroundColor(Color.red)
                    .invalidatableContent()
                Button("button press me", intent: TestIntent())
                Toggle("toggle press me", isOn: false, intent: TestIntent())
            }
        }
    }
}

struct widget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "kind", provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                widgetEntryView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
            } else {
                widgetEntryView(entry: entry)
                    .padding()
                    .background(Color.clear)
            }
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}
2.3 官方介绍了一个,在小组件中显示动态日期。

利用Text特性,官方地址

在小组件中显示动态日期 - 简体中文文档 - Apple Developer

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值