【iOS小组件实战】小组件旋转动画

474 阅读2分钟

前言

前两天有小伙伴问我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小组件实战】小组件旋转动画)

本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。