前言
前两天有小伙伴问我iOS小组件如何实现组件旋转效果,之前没有动手实现过,今天抽空了解一下并记录实现过程,末尾附代码链接。
实现
1.自定义旋转动画
通过旋转动画的 rotationEffect 属性实现动画旋转效果,需要配合计时器对旋转角度进行叠加驱动UI刷新
struct RotateAnimate: View {
var body: some View {
VStack {
Image(systemName: "clock")
.resizable()
.frame(width: 50, height: 50)
.rotationEffect(Angle(degrees: currentAngle))
Button(intent: RotateAnimateIntent()) {
Text("旋转按钮")
}
}
}
}
定义个一个计时器,每隔一定时间对旋转角度进行叠加,利用Button可交互组件的 intent 作为触发计时器的事件的入口。
private func tick(_ duration: TimeInterval) {
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
if (!currentAngleEnabled) {
return
}
withAnimation(.easeInOut(duration: 1.0)) {
currentAngle += 90 // 每次旋转90度
}
WidgetCenter.shared.reloadAllTimelines()
tick(duration)
}
}
struct RotateAnimateIntent: AppIntent {
static var title: LocalizedStringResource = "旋转按钮"
func perform() async throws -> some IntentResult {
currentAngleEnabled = !currentAngleEnabled
tick(0.25)
return .result()
}
}
配合使用可初步实现旋转效果,效果如下:
2.使用clockHandRotationEffect API
使用 clockHandRotationEffect 可以简单实现旋转动画效果,有个缺点就是无法动态配置,这里通过条件渲染处理。
struct RotateAnimate2: View {
var body: some View {
VStack {
// 旋转动画
if (isRotating) {
Image(systemName: "arrow.clockwise")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
.clockHandRotationEffect(period: .custom(1), in: TimeZone.current, anchor: .top)
} else {
Image(systemName: "arrow.clockwise")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
}
Button(intent: RotateIntent()) {
Text("旋转按钮")
}
}
}
}
在组件上配置 clockHandRotationEffect 即可看到如下效果:
3.clockHandRotationEffect其他动画效果
这里使用 clockHandRotationEffect 的另一种 modifier 形式实现的文字旋转动画
struct RotateAnimate4: View {
var body: some View {
// 文字翻转
Text("ClockRotationEffect")
.modifier(ClockHandRotationModifier(period: .secondHand, timeZone: TimeZone.current, anchor: .center))
}
}
该示例是别人基于 clockHandRotationEffect 实现的动画效果,详情见参考链接。
struct RectangleView2: View {
var body: some View {
ZStack(alignment: .center) {
Rectangle()
.frame(width: 75*2, height: 30)
.foregroundColor(.blue.opacity(0.1)) // 作者的代码透明度为0
Rectangle()
.frame(width: 90, height: 30)
.foregroundColor(.black.opacity(0.1)) // 作者的代码透明度为0
.offset(x: 75 / 2 + 7.5)
Rectangle()
.frame(width: 30, height: 30)
.foregroundColor(.black.opacity(1))
.modifier(ClockHandRotationModifier(period: ClockHandRotationPeriod.custom(-10), timeZone: TimeZone.current, anchor: .center))
.offset(x: 75)
Color.white
.frame(width: 1, height: 1)
.foregroundColor(.white)
}
.modifier(ClockHandRotationModifier(period: .custom(5), timeZone: TimeZone.current, anchor: .center))
}
}
struct RectangleView1: View {
var body: some View {
ZStack(alignment: .center) {
Rectangle()
.frame(width: 150, height: 30)
.foregroundColor(.red.opacity(0.1)) // 作者的代码透明度为0
Color.white
.frame(width: 1, height: 1)
.foregroundColor(.white)
}
}
}
struct RotateAnimate3: View {
var body: some View {
ZStack {
Rectangle()
.frame(width: 340, height: 1)
.foregroundColor(.red.opacity(1))
ZStack {
RectangleView1()
RectangleView2().offset(x: 75)
}
.modifier(ClockHandRotationModifier(period: .custom(-10), timeZone: TimeZone.current, anchor: .center))
}
}
}
项目链接
代码放到了github上,需要的自取
参考
友情链接
见原文:【iOS小组件实战】小组件旋转动画)
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。